
<!DOCTYPE html>
<html
  lang="zh-cn"
  data-figures=""
  
    class="page"
  
  
  >
  <head>
<title>内核启动流程（一） | 拾月国度</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<script>
  var _hmt = _hmt || [];
  (function() {
    var hm = document.createElement("script");
    hm.src = "https://hm.baidu.com/hm.js?c24bad48a3da8c3c532f0c580bb4f8c5";
    var s = document.getElementsByTagName("script")[0];
    s.parentNode.insertBefore(hm, s);
  })();
</script>





<meta property="og:locale" content="zh-cn" />

<meta property="og:type" content="article">
<meta name="description" content="head.S分析" />
<meta name="twitter:card" content="summary" />
<meta name="twitter:creator" content="">
<meta name="twitter:title" content="内核启动流程（一）" />
<meta name="twitter:image" content="https://kingdix10.gitee.io/covers/cover8.jpg"/>
<meta property="og:url" content="https://kingdix10.gitee.io/post/kernel/linux-6.1/boot/entry/" />
<meta property="og:title" content="内核启动流程（一）" />
<meta property="og:description" content="head.S分析" />
<meta property="og:image" content="https://kingdix10.gitee.io/covers/cover8.jpg" />

<link rel="apple-touch-icon" sizes="180x180" href="https://kingdix10.gitee.io/icons/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="https://kingdix10.gitee.io/icons/icon_yellow_transparent.png">
<link rel="manifest" href="https://kingdix10.gitee.io/icons/site.webmanifest">

<link rel="canonical" href="https://kingdix10.gitee.io/post/kernel/linux-6.1/boot/entry/">



<link rel="preload" href="https://kingdix10.gitee.io/css/styles.42e2c5f6d8cf9c52872666f8d8b2678ad0c426978b9d78aff3c33b7a1e7f6f97f54bcdaf0518a25fb0fe26367d04f8b07c683b3b38b331cb098daadee06b1f3e.css" integrity = "sha512-QuLF9tjPnFKHJmb42LJnitDEJpeLnXiv88M7eh5/b5f1S82vBRiiX7D&#43;JjZ9BPiwfGg7OzizMcsJjare4GsfPg==" as="style" crossorigin="anonymous">



<link rel="preload" href="https://kingdix10.gitee.io/zh-cn/js/bundle.352f6b8ac853443fc909370d968a769ffa303b36042cc66849f2d48d9e5dd951ee86c06130908f5d77ea0e55ec8fed0e5dc26f0f14bbd4ad6dcf35999232e5bc.js" as="script" integrity=
"sha512-NS9rishTRD/JCTcNlop2n/owOzYELMZoSfLUjZ5d2VHuhsBhMJCPXXfqDlXsj&#43;0OXcJvDxS71K1tzzWZkjLlvA==" crossorigin="anonymous">


<link rel="stylesheet" type="text/css" href="https://kingdix10.gitee.io/css/styles.42e2c5f6d8cf9c52872666f8d8b2678ad0c426978b9d78aff3c33b7a1e7f6f97f54bcdaf0518a25fb0fe26367d04f8b07c683b3b38b331cb098daadee06b1f3e.css" integrity="sha512-QuLF9tjPnFKHJmb42LJnitDEJpeLnXiv88M7eh5/b5f1S82vBRiiX7D&#43;JjZ9BPiwfGg7OzizMcsJjare4GsfPg==" crossorigin="anonymous">

  </head>
  <body
    data-code="9"
    data-lines="true"
    id="documentTop"
    data-lang="zh-cn"
  >

<header class="nav_header" >
  <nav class="nav"><a href='https://kingdix10.gitee.io/' class="nav_brand nav_item" title="拾月国度">
  <img src="https://kingdix10.gitee.io/logos/icon_yellow_transparent_72x72.png" class="logo" alt="拾月国度">
  <div class="nav_close">
    <div><svg class="icon">
  <title>open-menu</title>
  <use xlink:href="#open-menu"></use>
</svg>
<svg class="icon">
  <title>closeme</title>
  <use xlink:href="#closeme"></use>
</svg>
</div>
  </div>
</a>

    <div class='nav_body nav_body_left'>
      
      
      
        

  <div class="nav_parent">
    <a href="https://kingdix10.gitee.io/home" class="nav_item" title="主页">主页 </a>
  </div>
  <div class="nav_parent">
    <a href="https://kingdix10.gitee.io/" class="nav_item" title="归档">归档 </a>
  </div>
  <div class="nav_parent">
    <a href="https://kingdix10.gitee.io/" class="nav_item" title="链接">链接 <img src='https://kingdix10.gitee.io/icons/caret-icon.svg' alt="icon" class="nav_icon"></a>
    <div class="nav_sub">
      <span class="nav_child"></span>
        <a href="https://gitee.com/kingdix10" class="nav_child nav_item" title="Gitee">Gitee</a>
        <a href="https://github.com/kingdix10" class="nav_child nav_item" title="GitHub">GitHub</a>
        <a href="https://www.zhihu.com/people/kingdix10" class="nav_child nav_item" title="知乎">知乎</a>
        <a href="https://www.cnblogs.com/kingdix10" class="nav_child nav_item" title="博客园">博客园</a>
        <a href="https://blog.csdn.net/u014001096" class="nav_child nav_item" title="CSDN">CSDN</a>
    </div>
  </div>
  <div class="nav_parent">
    <a href="https://kingdix10.gitee.io/about/" class="nav_item" title="关于">关于 </a>
  </div>
      
      <div class="nav_parent">
        <a href="#" class="nav_item">🌐</a>
        <div class="nav_sub">
          <span class="nav_child"></span>
          
          <a href="https://kingdix10.gitee.io/" class="nav_child nav_item">中文</a>
          
          <a href="https://kingdix10.gitee.io/en/" class="nav_child nav_item">English</a>
          
        </div>
      </div>
<div class='follow'>
  <a href="https://gitee.com/kingdix10">
    <svg class="icon">
  <title>gitee</title>
  <use xlink:href="#gitee"></use>
</svg>

  </a>
  <a href="https://github.com/kingdix10">
    <svg class="icon">
  <title>github</title>
  <use xlink:href="#github"></use>
</svg>

  </a>
    
  <a href="https://kingdix10.gitee.io/index.xml">
    <svg class="icon">
  <title>rss</title>
  <use xlink:href="#rss"></use>
</svg>

  </a>
<div class="color_mode">
  <input type="checkbox" class="color_choice" id="mode">
</div>

</div>

    </div>
  </nav>
</header>

    <main>
<div class="grid-inverse wrap content">
  <article class="post_content">
    <h1 class="post_title">内核启动流程（一）</h1>
  <div class="post_meta">
    <span><svg class="icon">
  <title>calendar</title>
  <use xlink:href="#calendar"></use>
</svg>
</span>
    <span class="post_date">
      2020/03/15</span>
    <span class="post_time"> | 12 分钟阅读</span><span>&nbsp;|<a href='https://kingdix10.gitee.io/tags/linux%E5%86%85%E6%A0%B8/' title="linux内核" class="post_tag button button_translucent">linux内核
        </a><a href='https://kingdix10.gitee.io/tags/%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/' title="启动流程" class="post_tag button button_translucent">启动流程
        </a>
    </span>
    <span class="page_only">&nbsp;|
  <div class="post_share">
    分享到:

    <a href="https://kingdix10.gitee.io/post/kernel/linux-6.1/boot/entry/" title="Copy Link" class="link link_yank">
      <svg class="icon">
  <title>copy</title>
  <use xlink:href="#copy"></use>
</svg>

    </a>
  </div>
  
</span>
  </div>



      <div class="post_toc">
        <h2>文章目录</h2>
        <nav id="TableOfContents">
  <ul>
    <li><a href="#1-内核启动入口">1. 内核启动入口</a></li>
    <li><a href="#2-heads总览">2. head.S总览</a>
      <ul>
        <li><a href="#21-headtext">2.1. <code>.head.text</code></a></li>
        <li><a href="#22-inittext">2.2. <code>.init.text</code></a></li>
        <li><a href="#23-idmaptext">2.3. <code>.idmap.text</code></a></li>
      </ul>
    </li>
    <li><a href="#3-概览从入口到start_kernel">3. 概览：从入口到start_kernel</a></li>
    <li><a href="#4-mmu开启之前primary_entry">4. MMU开启之前：primary_entry</a>
      <ul>
        <li><a href="#41-preserve_boot_args">4.1. preserve_boot_args</a></li>
        <li><a href="#42-init_kernel_el">4.2. init_kernel_el</a>
          <ul>
            <li><a href="#421-init_el1">4.2.1. init_el1</a></li>
            <li><a href="#422-init_el2">4.2.2. init_el2</a>
              <ul>
                <li><a href="#4221-init_el2_state">4.2.2.1. init_el2_state</a></li>
              </ul>
            </li>
          </ul>
        </li>
        <li><a href="#43-create_idmap">4.3. create_idmap</a></li>
        <li><a href="#44-__cpu_setup">4.4. __cpu_setup</a></li>
      </ul>
    </li>
    <li><a href="#5-开启mmu__primary_switch">5. 开启MMU：__primary_switch</a>
      <ul>
        <li><a href="#51-__enable_mmu">5.1. <code>__enable_mmu</code></a></li>
        <li><a href="#52-clear_page_tables">5.2. clear_page_tables</a></li>
        <li><a href="#53-create_kernel_mapping">5.3. create_kernel_mapping</a></li>
      </ul>
    </li>
    <li><a href="#6-开启mmu之后__primary_switched">6. 开启MMU之后：__primary_switched</a>
      <ul>
        <li><a href="#61-初始化init_task栈空间">6.1. 初始化init_task栈空间</a>
          <ul>
            <li><a href="#611-init_cpu_task">6.1.1. init_cpu_task</a></li>
          </ul>
        </li>
        <li><a href="#62-中断向量基地址寄存器配置">6.2. 中断向量基地址寄存器配置</a></li>
        <li><a href="#63-备份fp和lr寄存器">6.3. 备份fp和lr寄存器</a></li>
        <li><a href="#64-保存设备树物理地址到__fdt_pointer">6.4. 保存设备树物理地址到__fdt_pointer</a></li>
        <li><a href="#65-计算kimage_voffset">6.5. 计算kimage_voffset</a></li>
        <li><a href="#66-set_cpu_boot_mode_flag">6.6. set_cpu_boot_mode_flag</a></li>
        <li><a href="#67-清空bss段">6.7. 清空BSS段</a></li>
        <li><a href="#68-一些其他工作">6.8. 一些其他工作</a></li>
        <li><a href="#69-early_fdt_map">6.9. early_fdt_map</a></li>
        <li><a href="#610-init_feature_override">6.10. init_feature_override</a></li>
        <li><a href="#611-finalise_el2">6.11. finalise_el2</a></li>
        <li><a href="#612-跳转start_kernel">6.12. 跳转start_kernel</a></li>
      </ul>
    </li>
    <li><a href="#7-参考资料">7. 参考资料</a></li>
  </ul>
</nav>
      </div>
    
    <div class="post_body"><h1 id="1-内核启动入口">1. 内核启动入口</h1>
<p>根据Makefile对KBUILD_LDS的定义，链接vmlinux使用的连接脚本为 <code>arch/$(SRCARCH)/kernel/vmlinux.lds</code>，实际上这个脚本是在编译过程中生成的。从链接脚本 <code>arch/arm64/kernel/vmlinux.lds</code>可以查到，程序的入口为 <code>_text</code>，镜像起始位置存放的是 <code>.head.text</code>段生成的指令。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="nf">OUTPUT_ARCH</span><span class="p">(</span><span class="n">aarch64</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="nf">ENTRY</span><span class="p">(</span><span class="n">_text</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="n">SECTIONS</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"> <span class="p">.</span> <span class="o">=</span> <span class="p">((((((</span><span class="o">-</span><span class="p">(((</span><span class="mi">1</span><span class="p">))</span> <span class="o">&lt;&lt;</span> <span class="p">((((</span><span class="mi">48</span><span class="p">)))</span> <span class="o">-</span> <span class="mi">1</span><span class="p">))))</span> <span class="o">+</span> <span class="p">(</span><span class="mh">0x08000000</span><span class="p">)))</span> <span class="o">+</span> <span class="p">(</span><span class="mh">0x08000000</span><span class="p">)));</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"> <span class="p">.</span><span class="n">head</span><span class="p">.</span><span class="nl">text</span> <span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  <span class="n">_text</span> <span class="o">=</span> <span class="p">.;</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  <span class="nf">KEEP</span><span class="p">(</span><span class="o">*</span><span class="p">(.</span><span class="n">head</span><span class="p">.</span><span class="n">text</span><span class="p">))</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"> <span class="p">...</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>也就是说，内核镜像的起始位置是 <code>.head.text</code>段生成的指令。</p>
<p>搜索 <code>.head.text</code>，可以找到 <code>include/linux/init.h</code>对 <code>__HEAD</code>定义 <code>.section    &quot;.head.text&quot;,&quot;ax&quot;</code>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln">1</span><span class="cl"><span class="cm">/* For assembly routines */</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="cp">#define __HEAD      .section    &#34;.head.text&#34;,&#34;ax&#34;
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="cp">#define __INIT      .section    &#34;.init.text&#34;,&#34;ax&#34;
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="cp">#define __FINIT     .previous
</span></span></span></code></pre></div><p>再看一下 <code>arch/arm64/kernel/vmlinux.lds</code>是怎么生成的，编译日志中，会有 <code>LDS     arch/arm64/kernel/vmlinux.lds</code>，<code>scripts/Makefile.build</code>中可以看到是对 <code>arch/arm64/kernel/vmlinux.lds.S</code>进行预处理得到了最终的链接脚本。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-makefile" data-lang="makefile"><span class="line"><span class="ln">1</span><span class="cl"><span class="c"># Linker scripts preprocessor (.lds.S -&gt; .lds)
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c"># ---------------------------------------------------------------------------
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="c"></span><span class="nv">quiet_cmd_cpp_lds_S</span> <span class="o">=</span> LDS     <span class="nv">$@</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">      <span class="nv">cmd_cpp_lds_S</span> <span class="o">=</span> <span class="k">$(</span>CPP<span class="k">)</span> <span class="k">$(</span>cpp_flags<span class="k">)</span> -P -U<span class="k">$(</span>ARCH<span class="k">)</span> <span class="se">\
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="se"></span>                             -D__ASSEMBLY__ -DLINKER_SCRIPT -o <span class="nv">$@</span> $&lt;
</span></span><span class="line"><span class="ln">6</span><span class="cl">
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="nf">$(obj)/%.lds</span><span class="o">:</span> <span class="k">$(</span><span class="nv">src</span><span class="k">)</span>/%.<span class="n">lds</span>.<span class="n">S</span> <span class="n">FORCE</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl">        <span class="k">$(</span>call if_changed_dep,cpp_lds_S<span class="k">)</span>
</span></span></code></pre></div><p>通过搜索 <code>__HEAD</code>，可以看到程序起始代码位于 <code>arch/arm64/kernel/head.S</code>。</p>
<h1 id="2-heads总览">2. head.S总览</h1>
<p>head.S可以分为三段，分别为 <code>.head.text</code>、<code>.init.text</code>和 <code>.idmap.text</code>。各个段包含的符号可以通过objdump或nm查看。</p>
<p><code>arch/arm64/boot/head.S</code>开始的注释简单说明了内核启动的条件。更详细的内容可以查看内核文档 <code>Documentation/arm64/booting.rst</code>或 <code>Documentation/translations/zh_CN/arm64/booting.txt</code>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="cm">/*
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="cm"> * Kernel startup entry point.
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="cm"> * ---------------------------
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="cm"> *
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="cm"> * The requirements are:
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="cm"> *   MMU = off, D-cache = off, I-cache = on or off,
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="cm"> *   x0 = physical address to the FDT blob.
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="cm"> *
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="cm"> * Note that the callee-saved registers are used for storing variables
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="cm"> * that are useful before the MMU is enabled. The allocations are described
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="cm"> * in the entry routines.
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="cm"> */</span>
</span></span></code></pre></div><h2 id="21-headtext">2.1. <code>.head.text</code></h2>
<p>顾名思义，这部分为头部内容，根据是否使能 <code>CONFIG_EFI</code>而有所不同，在未定义 <code>CONFIG_EFI</code>的情况下，实际工作只是跳转到 <code>primary_entry</code>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln"> 1</span><span class="cl">    <span class="n">__HEAD</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">    <span class="cm">/*
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="cm">     * DO NOT MODIFY. Image header expected by Linux boot-loaders.
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="cm">     */</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    <span class="n">efi_signature_nop</span>			<span class="c1">// special NOP to identity as PE/COFF executable
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1"></span>    <span class="n">b</span>	<span class="n">primary_entry</span>			<span class="c1">// branch to kernel start, magic
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"></span>    <span class="p">.</span><span class="n">quad</span>	<span class="mi">0</span>				<span class="c1">// Image load offset from start of RAM, little-endian
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1"></span>    <span class="n">le64sym</span>	<span class="n">_kernel_size_le</span>			<span class="c1">// Effective size of kernel image, little-endian
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1"></span>    <span class="n">le64sym</span>	<span class="n">_kernel_flags_le</span>		<span class="c1">// Informative flags, little-endian
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"></span>    <span class="p">.</span><span class="n">quad</span>	<span class="mi">0</span>				<span class="c1">// reserved
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c1"></span>    <span class="p">.</span><span class="n">quad</span>	<span class="mi">0</span>				<span class="c1">// reserved
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c1"></span>    <span class="p">.</span><span class="n">quad</span>	<span class="mi">0</span>				<span class="c1">// reserved
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="c1"></span>    <span class="p">.</span><span class="n">ascii</span>	<span class="n">ARM64_IMAGE_MAGIC</span>		<span class="c1">// Magic number
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="c1"></span>    <span class="p">.</span><span class="kt">long</span>	<span class="p">.</span><span class="n">Lpe_header_offset</span>		<span class="c1">// Offset to the PE header.
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">    <span class="n">__EFI_PE_HEADER</span>
</span></span></code></pre></div><h2 id="22-inittext">2.2. <code>.init.text</code></h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">primary_entry</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="n">preserve_boot_args</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="n">clear_page_tables</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="n">remap_region</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="n">create_idmap</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="n">create_kernel_mapping</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="n">__primary_switched</span>
</span></span></code></pre></div><h2 id="23-idmaptext">2.3. <code>.idmap.text</code></h2>
<p>用于恒等映射，需要编译为位置无关码。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="n">init_kernel_el</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="n">init_el1</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="n">init_el2</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="n">set_cpu_boot_mode_flag</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="n">secondary_holding_pen</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="n">secondary_entry</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="n">secondary_startup</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="n">__secondary_switched</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="n">__secondary_too_slow</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="n">__enable_mmu</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="n">__cpu_secondary_check52bitva</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="n">__no_granule_support</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="n">__primary_switch</span>
</span></span></code></pre></div><h1 id="3-概览从入口到start_kernel">3. 概览：从入口到start_kernel</h1>
<p>从入口到start_kernel的部分主要是汇编代码，后续的很多子系统都会依赖这部分代码做的初始化。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="o">+--</span> <span class="nf">_text</span><span class="p">()</span>                                 <span class="c1">/// 内核启动入口
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span>    <span class="err">\</span><span class="o">--</span> <span class="nf">primary_entry</span><span class="p">()</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">        <span class="o">+--</span> <span class="nf">preserve_boot_args</span><span class="p">()</span>            <span class="c1">/// 保存x0~x3到boot_args[0~3]
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"></span>        <span class="o">+--</span> <span class="nf">init_kernel_el</span><span class="p">()</span>                <span class="c1">/// 根据内核运行异常等级进行配置，返回启动模式
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1"></span>        <span class="o">|</span>   <span class="o">+--</span> <span class="nf">init_el1</span><span class="p">()</span>                  <span class="c1">/// 通常情况下从EL1启动内核
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1"></span>        <span class="o">|</span>   <span class="err">\</span><span class="o">--</span> <span class="nf">init_el2</span><span class="p">()</span>                  <span class="c1">/// 从EL2启动内核，用于开启VHE(Virtualization Host Extensions)
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"></span>        <span class="o">+--</span> <span class="nf">create_idmap</span><span class="p">()</span>                  <span class="c1">/// 建立恒等映射init_idmap_pg_dir和内核镜像映射init_pg_dir的页表
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1"></span>        <span class="o">+--</span> <span class="nf">__cpu_setup</span><span class="p">()</span>                   <span class="c1">/// 为开启MMU做的CPU初始化
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1"></span>        <span class="err">\</span><span class="o">--</span> <span class="nf">__primary_switch</span><span class="p">()</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">            <span class="o">+--</span> <span class="nf">__enable_mmu</span><span class="p">()</span>              <span class="c1">/// 开启MMU，将init_idmap_pg_dir加载到ttbr0，reserved_pg_dir加载到ttbr1
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c1"></span>            <span class="o">+--</span> <span class="nf">clear_page_tables</span><span class="p">()</span>         <span class="c1">/// 清空init_pg_dir
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c1"></span>            <span class="o">+--</span> <span class="nf">create_kernel_mapping</span><span class="p">()</span>     <span class="c1">/// 填充init_pg_dir
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="c1"></span>            <span class="o">+--</span> <span class="nf">load_ttbr1</span><span class="p">()</span>                <span class="c1">/// 将init_pg_dir加载到ttbr1
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="c1"></span>            <span class="err">\</span><span class="o">--</span> <span class="nf">__primary_switched</span><span class="p">()</span>        <span class="c1">/// 初始化init_task栈，设置VBAR_EL1，保存FDT地址，计算kimage_voffset，清空bss段
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="c1"></span>                <span class="o">+--</span> <span class="nf">set_cpu_boot_mode_flag</span><span class="p">()</span><span class="c1">/// 设置__boot_cpu_mode变量
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="c1"></span>                <span class="o">+--</span> <span class="nf">early_fdt_map</span><span class="p">()</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">                <span class="o">|</span>   <span class="o">+--</span> <span class="nf">early_fixmap_init</span><span class="p">()</span> <span class="c1">/// 尝试建立fixmap的页表，可能失败，后边init_feature_override会用到
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="c1"></span>                <span class="o">|</span>   <span class="err">\</span><span class="o">--</span> <span class="nf">fixmap_remap_fdt</span><span class="p">()</span>  <span class="c1">/// 如果成功建立fixmap页表，将fdt映射到fixmap的FIX_FDT区域
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="c1"></span>                <span class="o">+--</span> <span class="nf">init_feature_override</span><span class="p">()</span> <span class="c1">/// 根据BootLoader传入的bootargs，对一些参数的改写
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="c1"></span>                <span class="o">+--</span> <span class="nf">finalise_el2</span><span class="p">()</span>          <span class="c1">/// Prefer VHE if possible
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="c1"></span>                <span class="err">\</span><span class="o">--</span> <span class="nf">start_kernel</span><span class="p">()</span>          <span class="c1">/// 跳转到start_kernel执行
</span></span></span></code></pre></div><h1 id="4-mmu开启之前primary_entry">4. MMU开启之前：primary_entry</h1>
<p>在内核启动入口直接跳转到primary_entry，这是MMU开启之前所有函数的总流程。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln"> 1</span><span class="cl">    <span class="cm">/*
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="cm">     * The following callee saved general purpose registers are used on the
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="cm">     * primary lowlevel boot path:
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="cm">     *
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="cm">     *  Register   Scope                      Purpose
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="cm">     *  x20        primary_entry() .. __primary_switch()    CPU boot mode
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="cm">     *  x21        primary_entry() .. start_kernel()        FDT pointer passed at boot in x0
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="cm">     *  x22        create_idmap() .. start_kernel()         ID map VA of the DT blob
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="cm">     *  x23        primary_entry() .. start_kernel()        physical misalignment/KASLR offset
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="cm">     *  x24        __primary_switch()                       linear map KASLR seed
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="cm">     *  x25        primary_entry() .. start_kernel()        supported VA size
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="cm">     *  x28        create_idmap()                           callee preserved temp register
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="cm">     */</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="nf">SYM_CODE_START</span><span class="p">(</span><span class="n">primary_entry</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">    <span class="n">bl</span>	<span class="n">preserve_boot_args</span>  <span class="c1">/// 保存启动参数到boot_args数组
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="c1"></span>    <span class="n">bl</span>	<span class="n">init_kernel_el</span>      <span class="c1">/// 返回时，w0=cpu_boot_mode
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="c1"></span>    <span class="n">mov</span>	<span class="n">x20</span><span class="p">,</span> <span class="n">x0</span>             <span class="c1">/// 保存cpu_boot_mode到x20
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="c1"></span>    <span class="n">bl</span>	<span class="n">create_idmap</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">
</span></span><span class="line"><span class="ln">20</span><span class="cl">    <span class="cm">/*
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="cm">     * The following calls CPU setup code, see arch/arm64/mm/proc.S for
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="cm">     * details.
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="cm">     * On return, the CPU will be ready for the MMU to be turned on and
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="cm">     * the TCR will have been set.
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="cm">     */</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="cp">#if VA_BITS &gt; 48
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="cp"></span>    <span class="n">mrs_s</span>	<span class="n">x0</span><span class="p">,</span> <span class="n">SYS_ID_AA64MMFR2_EL1</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">    <span class="n">tst</span>	<span class="n">x0</span><span class="p">,</span> <span class="err">#</span><span class="mh">0xf</span> <span class="o">&lt;&lt;</span> <span class="n">ID_AA64MMFR2_LVA_SHIFT</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl">    <span class="n">mov</span>	<span class="n">x0</span><span class="p">,</span> <span class="err">#</span><span class="n">VA_BITS</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl">    <span class="n">mov</span>	<span class="n">x25</span><span class="p">,</span> <span class="err">#</span><span class="n">VA_BITS_MIN</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">    <span class="n">csel</span>	<span class="n">x25</span><span class="p">,</span> <span class="n">x25</span><span class="p">,</span> <span class="n">x0</span><span class="p">,</span> <span class="n">eq</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl">    <span class="n">mov</span>	<span class="n">x0</span><span class="p">,</span> <span class="n">x25</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="cp">#endif
</span></span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="cp"></span>    <span class="n">bl</span>	<span class="n">__cpu_setup</span>         <span class="c1">/// 为开启MMU做的CPU初始化
</span></span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="c1"></span>    <span class="n">b</span>	<span class="n">__primary_switch</span>
</span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="nf">SYM_CODE_END</span><span class="p">(</span><span class="n">primary_entry</span><span class="p">)</span>
</span></span></code></pre></div><h2 id="41-preserve_boot_args">4.1. preserve_boot_args</h2>
<p>主要工作是将FDT的基地址保存到x21寄存器，将启动参数（x0~x3）保存到boot_args数组。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="cm">/*
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="cm"> * Preserve the arguments passed by the bootloader in x0 .. x3
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="cm"> */</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="nf">SYM_CODE_START_LOCAL</span><span class="p">(</span><span class="n">preserve_boot_args</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    <span class="n">mov</span>	<span class="n">x21</span><span class="p">,</span> <span class="n">x0</span>			<span class="c1">/// x21=FDT，x0是uboot传入的第一个参数，记录fdt的基地址，将x0的值保存到x21寄存器备份
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">    <span class="n">adr_l</span>	<span class="n">x0</span><span class="p">,</span> <span class="n">boot_args</span>       <span class="c1">/// 读取boot_args变量的当前地址到x0，此时MMU处于关闭状态，访问的是物理地址
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1"></span>    <span class="n">stp</span>	<span class="n">x21</span><span class="p">,</span> <span class="n">x1</span><span class="p">,</span> <span class="p">[</span><span class="n">x0</span><span class="p">]</span>       <span class="c1">/// record the contents of, x0 .. x3 at kernel entry
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1"></span>    <span class="n">stp</span>	<span class="n">x2</span><span class="p">,</span> <span class="n">x3</span><span class="p">,</span> <span class="p">[</span><span class="n">x0</span><span class="p">,</span> <span class="err">#</span><span class="mi">16</span><span class="p">]</span>   <span class="c1">/// 将x0~x3保存到boot_args[0~3]
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="n">dmb</span>	<span class="n">sy</span>                  <span class="c1">/// 确保stp指令完成
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="n">add</span>	<span class="n">x1</span><span class="p">,</span> <span class="n">x0</span><span class="p">,</span> <span class="err">#</span><span class="mh">0x20</span>       <span class="c1">/// 4 x 8 bytes，boot_args数组的大小
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="c1"></span>    <span class="n">b</span>	<span class="n">dcache_inval_poc</span>    <span class="c1">/// 使boot_args[]数组对应的高速缓存失效
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="c1"></span><span class="nf">SYM_CODE_END</span><span class="p">(</span><span class="n">preserve_boot_args</span><span class="p">)</span>
</span></span></code></pre></div><p>adr_l是在 <code>arch/arm64/include/asm/assembler.h</code>定义的一个宏，用于操作当前PC值前后4G范围内存。</p>
<p><code>dmb sy</code>在全系统高速缓冲范围内做一次内存屏障，保证前面的stp指令运行顺序正确，保证stp在调用dcache_inval_poc前完成。
<code>dcache_inval_poc</code>传入参数为boot_args数组的起始和结束地址，函数的作用是使boot_args数组对应的高速缓存失效，并清除这些缓存。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="cm">/*
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="cm"> *	dcache_inval_poc(start, end)
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="cm"> *
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="cm"> * 	Ensure that any D-cache lines for the interval [start, end)
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="cm"> * 	are invalidated. Any partial lines at the ends of the interval are
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="cm"> *	also cleaned to PoC to prevent data loss.
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="cm"> *
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="cm"> *	- start   - kernel start address of region
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="cm"> *	- end     - kernel end address of region
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="cm"> */</span>
</span></span></code></pre></div><h2 id="42-init_kernel_el">4.2. init_kernel_el</h2>
<p>判断启动的模式是EL2还是非安全模式的EL1，并进行相关级别的系统配置(ARMv8中EL2是hypervisor模式，EL1是标准的内核模式)，然后使用w0返回启动模式（BOOT_CPU_MODE_EL1或BOOT_CPU_MODE_EL2）。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln">1</span><span class="cl"><span class="cp">#define BOOT_CPU_MODE_EL1	(0xe11)
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="cp">#define BOOT_CPU_MODE_EL2	(0xe12)
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="cm">/* Current Exception Level values, as contained in CurrentEL */</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="cp">#define CurrentEL_EL1		(1 &lt;&lt; 2)
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="cp">#define CurrentEL_EL2		(2 &lt;&lt; 2)
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="cm">/*
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="cm"> * Starting from EL2 or EL1, configure the CPU to execute at the highest
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="cm"> * reachable EL supported by the kernel in a chosen default state. If dropping
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="cm"> * from EL2 to EL1, configure EL2 before configuring EL1.
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="cm"> *
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="cm"> * Since we cannot always rely on ERET synchronizing writes to sysregs (e.g. if
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="cm"> * SCTLR_ELx.EOS is clear), we place an ISB prior to ERET.
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="cm"> *
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="cm"> * Returns either BOOT_CPU_MODE_EL1 or BOOT_CPU_MODE_EL2 in x0 if
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="cm"> * booted in EL1 or EL2 respectively, with the top 32 bits containing
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="cm"> * potential context flags. These flags are *not* stored in __boot_cpu_mode.
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="cm"> */</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="nf">SYM_FUNC_START</span><span class="p">(</span><span class="n">init_kernel_el</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">    <span class="n">mrs</span>	<span class="n">x0</span><span class="p">,</span> <span class="n">CurrentEL</span>           <span class="c1">/// 读取当前EL等级
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="c1"></span>    <span class="n">cmp</span>	<span class="n">x0</span><span class="p">,</span> <span class="err">#</span><span class="n">CurrentEL_EL2</span>      <span class="c1">/// 如果是EL2则跳转到init_el2，否则继续向下执行init_el1的代码
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="c1"></span>    <span class="n">b</span><span class="p">.</span><span class="n">eq</span>	<span class="n">init_el2</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="nf">SYM_INNER_LABEL</span><span class="p">(</span><span class="n">init_el1</span><span class="p">,</span> <span class="n">SYM_L_LOCAL</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">    <span class="p">...</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">    <span class="n">eret</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="nf">SYM_INNER_LABEL</span><span class="p">(</span><span class="n">init_el2</span><span class="p">,</span> <span class="n">SYM_L_LOCAL</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">    <span class="p">...</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">    <span class="n">eret</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="nf">SYM_FUNC_END</span><span class="p">(</span><span class="n">init_kernel_el</span><span class="p">)</span>
</span></span></code></pre></div><h3 id="421-init_el1">4.2.1. init_el1</h3>
<p>配置CPU的大小端模式，将启动模式BOOT_CPU_MODE_EL1写入w0，然后返回到primary_entry。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln">1</span><span class="cl"><span class="cp">#define INIT_SCTLR_EL1_MMU_OFF \
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="cp">    (ENDIAN_SET_EL1 | SCTLR_EL1_LSMAOE | SCTLR_EL1_nTLSMD | \
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="cp">     SCTLR_EL1_EIS  | SCTLR_EL1_TSCXT  | SCTLR_EL1_EOS)
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="cp">#define INIT_PSTATE_EL1 \
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="cp">    (PSR_D_BIT | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT | PSR_MODE_EL1h)
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln">1</span><span class="cl"><span class="nf">SYM_INNER_LABEL</span><span class="p">(</span><span class="n">init_el1</span><span class="p">,</span> <span class="n">SYM_L_LOCAL</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">    <span class="n">mov_q</span>	<span class="n">x0</span><span class="p">,</span> <span class="n">INIT_SCTLR_EL1_MMU_OFF</span>      <span class="c1">/// MMU关闭时，对sctlr_el1的赋值
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="c1"></span>    <span class="n">msr</span>	<span class="n">sctlr_el1</span><span class="p">,</span> <span class="n">x0</span>                       <span class="c1">/// 配置CPU的大小端模式，EE域用来配置EL1，E0E域用来配置EL0
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"></span>    <span class="n">isb</span>                                     <span class="c1">/// 配置CPU大小端模式后，确保前面的指令都运行完成
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1"></span>    <span class="n">mov_q</span>	<span class="n">x0</span><span class="p">,</span> <span class="n">INIT_PSTATE_EL1</span>             <span class="c1">///
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="c1"></span>    <span class="n">msr</span>	<span class="n">spsr_el1</span><span class="p">,</span> <span class="n">x0</span>                        <span class="c1">/// 将INIT_PSTATE_EL1写入spsr_el1
</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="c1"></span>    <span class="n">msr</span>	<span class="n">elr_el1</span><span class="p">,</span> <span class="n">lr</span>                         <span class="c1">/// 将返回地址写入elr_el1，lr是primary_entry中`bl init_kernel_el`的下一条指令地址
</span></span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="c1"></span>    <span class="n">mov</span>	<span class="n">w0</span><span class="p">,</span> <span class="err">#</span><span class="n">BOOT_CPU_MODE_EL1</span>              <span class="c1">/// 记录启动模式
</span></span></span><span class="line"><span class="ln">9</span><span class="cl"><span class="c1"></span>    <span class="n">eret</span>                                    <span class="c1">/// 通过eret来使用ELR_ELx和SPSR_ELx来恢复PC和PSTATE
</span></span></span></code></pre></div><h3 id="422-init_el2">4.2.2. init_el2</h3>
<p>vhe的全称是Virtualization Host Extension support。是armv8.1的新特性，其最终要就是支持type-2的hypervisors 这种扩展让kernel直接跑在el2上，这样可以减少host和guest之间share的寄存器，并减少overhead of virtualization 具体实现如下面的<a href="https://lwn.net/Articles/650524">https://lwn.net/Articles/650524</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="nf">SYM_INNER_LABEL</span><span class="p">(</span><span class="n">init_el2</span><span class="p">,</span> <span class="n">SYM_L_LOCAL</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">    <span class="n">mov_q</span>	<span class="n">x0</span><span class="p">,</span> <span class="n">HCR_HOST_NVHE_FLAGS</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">    <span class="n">msr</span>	<span class="n">hcr_el2</span><span class="p">,</span> <span class="n">x0</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="n">isb</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">    <span class="n">init_el2_state</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    <span class="cm">/* Hypervisor stub */</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="n">adr_l</span>	<span class="n">x0</span><span class="p">,</span> <span class="n">__hyp_stub_vectors</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="n">msr</span>	<span class="n">vbar_el2</span><span class="p">,</span> <span class="n">x0</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="n">isb</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="n">mov_q</span>	<span class="n">x1</span><span class="p">,</span> <span class="n">INIT_SCTLR_EL1_MMU_OFF</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl">    <span class="cm">/*
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="cm">     * Fruity CPUs seem to have HCR_EL2.E2H set to RES1,
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="cm">     * making it impossible to start in nVHE mode. Is that
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="cm">     * compliant with the architecture? Absolutely not!
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="cm">     */</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">    <span class="n">mrs</span>	<span class="n">x0</span><span class="p">,</span> <span class="n">hcr_el2</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">    <span class="n">and</span>	<span class="n">x0</span><span class="p">,</span> <span class="n">x0</span><span class="p">,</span> <span class="err">#</span><span class="n">HCR_E2H</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">    <span class="n">cbz</span>	<span class="n">x0</span><span class="p">,</span> <span class="mf">1f</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">
</span></span><span class="line"><span class="ln">24</span><span class="cl">    <span class="cm">/* Set a sane SCTLR_EL1, the VHE way */</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">    <span class="n">msr_s</span>	<span class="n">SYS_SCTLR_EL12</span><span class="p">,</span> <span class="n">x1</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">    <span class="n">mov</span>	<span class="n">x2</span><span class="p">,</span> <span class="err">#</span><span class="n">BOOT_CPU_FLAG_E2H</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">    <span class="n">b</span>	<span class="mf">2f</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">
</span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="mi">1</span><span class="o">:</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl">    <span class="n">msr</span>	<span class="n">sctlr_el1</span><span class="p">,</span> <span class="n">x1</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">    <span class="n">mov</span>	<span class="n">x2</span><span class="p">,</span> <span class="n">xzr</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="mi">2</span><span class="o">:</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl">    <span class="n">msr</span>	<span class="n">elr_el2</span><span class="p">,</span> <span class="n">lr</span>
</span></span><span class="line"><span class="ln">34</span><span class="cl">    <span class="n">mov</span>	<span class="n">w0</span><span class="p">,</span> <span class="err">#</span><span class="n">BOOT_CPU_MODE_EL2</span>
</span></span><span class="line"><span class="ln">35</span><span class="cl">    <span class="n">orr</span>	<span class="n">x0</span><span class="p">,</span> <span class="n">x0</span><span class="p">,</span> <span class="n">x2</span>
</span></span><span class="line"><span class="ln">36</span><span class="cl">    <span class="n">eret</span>
</span></span><span class="line"><span class="ln">37</span><span class="cl"><span class="nf">SYM_FUNC_END</span><span class="p">(</span><span class="n">init_kernel_el</span><span class="p">)</span>
</span></span></code></pre></div><h4 id="4221-init_el2_state">4.2.2.1. init_el2_state</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="cm">/**
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="cm"> * Initialize EL2 registers to sane values. This should be called early on all
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="cm"> * cores that were booted in EL2. Note that everything gets initialised as
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="cm"> * if VHE was not evailable. The kernel context will be upgraded to VHE
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="cm"> * if possible later on in the boot process
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="cm"> *
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="cm"> * Regs: x0, x1 and x2 are clobbered.
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="cm"> */</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="p">.</span><span class="n">macro</span> <span class="n">init_el2_state</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="n">__init_el2_sctlr</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="n">__init_el2_timers</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">    <span class="n">__init_el2_debug</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="n">__init_el2_lor</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">    <span class="n">__init_el2_stage2</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">    <span class="n">__init_el2_gicv3</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">    <span class="n">__init_el2_hstr</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">    <span class="n">__init_el2_nvhe_idregs</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">    <span class="n">__init_el2_nvhe_cptr</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">    <span class="n">__init_el2_fgt</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">    <span class="n">__init_el2_nvhe_prepare_eret</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="p">.</span><span class="n">endm</span>
</span></span></code></pre></div><h2 id="43-create_idmap">4.3. create_idmap</h2>
<p>填充init_idmap_pg_dir和init_pg_dir页表，这个时候的映射是以块为单位的，每个块大小为2M。在开启MMU时，init_idmap_pg_dir会被加载到ttbr0。</p>
<p>init_idmap_pg_dir用于恒等映射，就是虚拟地址和物理地址相同的映射。linux-6.1的init_idmap_pg_dir替代了早期版本的idmap_pg_dir。之前版本的idmap_pg_dir只会映射idmap.text段，而init_idmap_pg_dir会映射整个内核镜像，在内核镜像之后，还会映射FDT，所以init_idmap_pg_dir映射的空间会比内核镜像大一些。
create_idmap首先将整个区间（包含内核镜像和FDT）映射为RX属性，再将init_pg_dir~init_pg_end重新映射为RW属性，最后把FDT以RW属性映射到内核镜像之后。</p>
<h2 id="44-__cpu_setup">4.4. __cpu_setup</h2>
<p>代码位于 <code>arch/arm64/mm/proc.S</code>。是为开启MMU而初始化处理器相关的代码，配置MMU，配置访问权限，内存地址划分等。
在虚拟地址小于48bit时，调用此函数前，x0记录虚拟地址位数。函数返回时x0记录了SCTLR_EL1要写入的值，最后传给__enable_mmu。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="cm">/*
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="cm"> *	__cpu_setup
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="cm"> *
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="cm"> *	Initialise the processor for turning the MMU on.
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="cm"> *
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="cm"> * Input:
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="cm"> *	x0 - actual number of VA bits (ignored unless VA_BITS &gt; 48)
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="cm"> * Output:
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="cm"> *	Return in x0 the value of the SCTLR_EL1 register.
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="cm"> */</span>
</span></span></code></pre></div><h1 id="5-开启mmu__primary_switch">5. 开启MMU：__primary_switch</h1>
<p><code>__primary_switch</code>表示重要的切换，这个非常重要的切换就是开启MMU。开启MMU(<code>__enable_mmu</code>)前，CPU访问的是物理地址，<code>__enable_mmu</code>之后，CPU访问的是虚拟地址。这个虚拟地址最高位为0，使用的是TTBR0，而此时TTBR0执行的页表是init_idmap_pg_dir。而reserved_pg_dir这个时候还没有填充。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="nf">SYM_FUNC_START_LOCAL</span><span class="p">(</span><span class="n">__primary_switch</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">    <span class="n">adrp</span>	<span class="n">x1</span><span class="p">,</span> <span class="n">reserved_pg_dir</span>     <span class="c1">/// ttbr1
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1"></span>    <span class="n">adrp</span>	<span class="n">x2</span><span class="p">,</span> <span class="n">init_idmap_pg_dir</span>   <span class="c1">/// ttbr0
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"></span>    <span class="n">bl</span>	<span class="n">__enable_mmu</span>                <span class="c1">/// 开启MMU
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1"></span><span class="cp">#ifdef CONFIG_RELOCATABLE
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="cp"></span>    <span class="n">adrp</span>	<span class="n">x23</span><span class="p">,</span> <span class="n">KERNEL_START</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">    <span class="n">and</span>	<span class="n">x23</span><span class="p">,</span> <span class="n">x23</span><span class="p">,</span> <span class="n">MIN_KIMG_ALIGN</span> <span class="o">-</span> <span class="mi">1</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="cp">#ifdef CONFIG_RANDOMIZE_BASE
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="cp"></span>    <span class="n">mov</span>	<span class="n">x0</span><span class="p">,</span> <span class="n">x22</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="n">adrp</span>	<span class="n">x1</span><span class="p">,</span> <span class="n">init_pg_end</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="n">mov</span>	<span class="n">sp</span><span class="p">,</span> <span class="n">x1</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">    <span class="n">mov</span>	<span class="n">x29</span><span class="p">,</span> <span class="n">xzr</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="n">bl</span>	<span class="n">__pi_kaslr_early_init</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">    <span class="n">and</span>	<span class="n">x24</span><span class="p">,</span> <span class="n">x0</span><span class="p">,</span> <span class="err">#</span><span class="n">SZ_2M</span> <span class="o">-</span> <span class="mi">1</span>		<span class="c1">// capture memstart offset seed
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="c1"></span>    <span class="n">bic</span>	<span class="n">x0</span><span class="p">,</span> <span class="n">x0</span><span class="p">,</span> <span class="err">#</span><span class="n">SZ_2M</span> <span class="o">-</span> <span class="mi">1</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">    <span class="n">orr</span>	<span class="n">x23</span><span class="p">,</span> <span class="n">x23</span><span class="p">,</span> <span class="n">x0</span>			<span class="c1">// record kernel offset
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="c1"></span><span class="cp">#endif
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="cp">#endif
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="cp"></span>    <span class="n">bl</span>	<span class="n">clear_page_tables</span>           <span class="c1">/// 清空init_pg_dir
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="c1"></span>    <span class="n">bl</span>	<span class="n">create_kernel_mapping</span>       <span class="c1">/// 填充init_pg_dir
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">    <span class="n">adrp</span>	<span class="n">x1</span><span class="p">,</span> <span class="n">init_pg_dir</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">    <span class="n">load_ttbr1</span> <span class="n">x1</span><span class="p">,</span> <span class="n">x1</span><span class="p">,</span> <span class="n">x2</span>           <span class="c1">/// 加载init_pg_dir到ttbr1，为跳转到新的地址做准备
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="c1"></span><span class="cp">#ifdef CONFIG_RELOCATABLE
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="cp"></span>    <span class="n">bl</span>	<span class="n">__relocate_kernel</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="cp">#endif
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="cp"></span>    <span class="n">ldr</span>	<span class="n">x8</span><span class="p">,</span> <span class="o">=</span><span class="n">__primary_switched</span>     <span class="c1">/// __primary_switched编译时的地址，如果使能了CONFIG_RELOCATABLE，__relocate_kernel会给__primary_switched加上一个偏移
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="c1"></span>    <span class="n">adrp</span>	<span class="n">x0</span><span class="p">,</span> <span class="n">KERNEL_START</span>		<span class="c1">// __pa(KERNEL_START)   /// 运行时的物理地址
</span></span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="c1"></span>    <span class="n">br</span>	<span class="n">x8</span>                          <span class="c1">/// 跳转到__primary_switched虚拟地址运行
</span></span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="c1"></span><span class="nf">SYM_FUNC_END</span><span class="p">(</span><span class="n">__primary_switch</span><span class="p">)</span>
</span></span></code></pre></div><p>在 <code>__primary_switch</code>的最后，使用 <code>adrp x0, KERNEL_START</code>记录了 <code>_text</code>的地址，也就是内核镜像起始的物理地址。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln">1</span><span class="cl"><span class="cp">#define KERNEL_START		_text
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="cp">#define KERNEL_END		_end
</span></span></span></code></pre></div><h2 id="51-__enable_mmu">5.1. <code>__enable_mmu</code></h2>
<p>主要工作：</p>
<ol>
<li>检查CPU是否支持软件设置的页面大小，如果不支持，CPU会在停止在这里。</li>
<li>将init_idmap_pg_dir和reserved_pg_dir分别加载到TTBR0_EL1和TTBR1_EL1。</li>
<li>开启MMU，并使本地icache失效。</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="cm">/*
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="cm"> * Enable the MMU.
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="cm"> *
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="cm"> *  x0  = SCTLR_EL1 value for turning on the MMU.
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="cm"> *  x1  = TTBR1_EL1 value
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="cm"> *  x2  = ID map root table address
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="cm"> *
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="cm"> * Returns to the caller via x30/lr. This requires the caller to be covered
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="cm"> * by the .idmap.text section.
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="cm"> *
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="cm"> * Checks if the selected granule size is supported by the CPU.
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="cm"> * If it isn&#39;t, park the CPU
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="cm"> */</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="nf">SYM_FUNC_START</span><span class="p">(</span><span class="n">__enable_mmu</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">    <span class="n">mrs</span>	<span class="n">x3</span><span class="p">,</span> <span class="n">ID_AA64MMFR0_EL1</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">    <span class="n">ubfx</span>	<span class="n">x3</span><span class="p">,</span> <span class="n">x3</span><span class="p">,</span> <span class="err">#</span><span class="n">ID_AA64MMFR0_EL1_TGRAN_SHIFT</span><span class="p">,</span> <span class="mi">4</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">    <span class="n">cmp</span>     <span class="n">x3</span><span class="p">,</span> <span class="err">#</span><span class="n">ID_AA64MMFR0_EL1_TGRAN_SUPPORTED_MIN</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">    <span class="n">b</span><span class="p">.</span><span class="n">lt</span>    <span class="n">__no_granule_support</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">    <span class="n">cmp</span>     <span class="n">x3</span><span class="p">,</span> <span class="err">#</span><span class="n">ID_AA64MMFR0_EL1_TGRAN_SUPPORTED_MAX</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">    <span class="n">b</span><span class="p">.</span><span class="n">gt</span>    <span class="n">__no_granule_support</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">    <span class="n">phys_to_ttbr</span> <span class="n">x2</span><span class="p">,</span> <span class="n">x2</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">    <span class="n">msr</span>	<span class="n">ttbr0_el1</span><span class="p">,</span> <span class="n">x2</span>			<span class="c1">// load TTBR0
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="c1"></span>    <span class="n">load_ttbr1</span> <span class="n">x1</span><span class="p">,</span> <span class="n">x1</span><span class="p">,</span> <span class="n">x3</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">
</span></span><span class="line"><span class="ln">25</span><span class="cl">    <span class="n">set_sctlr_el1</span>	<span class="n">x0</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">
</span></span><span class="line"><span class="ln">27</span><span class="cl">    <span class="n">ret</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="nf">SYM_FUNC_END</span><span class="p">(</span><span class="n">__enable_mmu</span><span class="p">)</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln">1</span><span class="cl"><span class="nf">SYM_FUNC_START_LOCAL</span><span class="p">(</span><span class="n">__no_granule_support</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">    <span class="cm">/* Indicate that this CPU can&#39;t boot and is stuck in the kernel */</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">    <span class="n">update_early_cpu_boot_status</span> \
</span></span><span class="line"><span class="ln">4</span><span class="cl">        <span class="n">CPU_STUCK_IN_KERNEL</span> <span class="o">|</span> <span class="n">CPU_STUCK_REASON_NO_GRAN</span><span class="p">,</span> <span class="n">x1</span><span class="p">,</span> <span class="n">x2</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="mi">1</span><span class="o">:</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">    <span class="n">wfe</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl">    <span class="n">wfi</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl">    <span class="n">b</span>	<span class="mi">1</span><span class="n">b</span>
</span></span><span class="line"><span class="ln">9</span><span class="cl"><span class="nf">SYM_FUNC_END</span><span class="p">(</span><span class="n">__no_granule_support</span><span class="p">)</span>
</span></span></code></pre></div><h2 id="52-clear_page_tables">5.2. clear_page_tables</h2>
<p>create_idmap填充了init_pg_dir，这里对init_pg_dir进行清零。</p>
<p><code>memset(init_pg_dir, 0, init_pg_end - init_pg_dir)</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="nf">SYM_FUNC_START_LOCAL</span><span class="p">(</span><span class="n">clear_page_tables</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">    <span class="cm">/*
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="cm">     * Clear the init page tables.
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="cm">     */</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    <span class="n">adrp</span>	<span class="n">x0</span><span class="p">,</span> <span class="n">init_pg_dir</span> <span class="c1">/// 起始地址
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1"></span>    <span class="n">adrp</span>	<span class="n">x1</span><span class="p">,</span> <span class="n">init_pg_end</span> <span class="c1">/// 结束地址
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"></span>    <span class="n">sub</span>	<span class="n">x2</span><span class="p">,</span> <span class="n">x1</span><span class="p">,</span> <span class="n">x0</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    <span class="n">mov</span>	<span class="n">x1</span><span class="p">,</span> <span class="n">xzr</span>             <span class="c1">/// xzr是一个特殊寄存器，读取时值为0
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1"></span>    <span class="n">b</span>	<span class="n">__pi_memset</span>			<span class="c1">// tail call
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"></span><span class="nf">SYM_FUNC_END</span><span class="p">(</span><span class="n">clear_page_tables</span><span class="p">)</span>
</span></span></code></pre></div><h2 id="53-create_kernel_mapping">5.3. create_kernel_mapping</h2>
<p>创建内核镜像映射，填充init_pg_dir，注意此时还是使用的块映射。</p>
<h1 id="6-开启mmu之后__primary_switched">6. 开启MMU之后：__primary_switched</h1>
<p>开启MMU之后，CPU访问的是虚拟地址。</p>
<ol>
<li>准备0号进程内核栈</li>
<li>VBAR_EL1中断向量表配置</li>
<li>计算kimage_voffset</li>
<li>保存CPU启动模式</li>
<li>清空BSS段</li>
<li>尝试fixmap映射</li>
<li>解析boot_args中CPU相关的一些特性，并修改默认值</li>
<li>跳转到start_kernel。</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln">1</span><span class="cl"><span class="cm">/*
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="cm"> * The following fragment of code is executed with the MMU enabled.
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="cm"> *
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="cm"> *   x0 = __pa(KERNEL_START)
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="cm"> */</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="nf">SYM_FUNC_START_LOCAL</span><span class="p">(</span><span class="n">__primary_switched</span><span class="p">)</span>
</span></span></code></pre></div><h2 id="61-初始化init_task栈空间">6.1. 初始化init_task栈空间</h2>
<p>主要工作：</p>
<ol>
<li>设置SP_EL0、SP_ELx、x29(FP)寄存器，配置init_task的栈</li>
<li>将per_cpu_offset写入TPIDR_ELx</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln">1</span><span class="cl">    <span class="n">adr_l</span>	<span class="n">x4</span><span class="p">,</span> <span class="n">init_task</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">    <span class="n">init_cpu_task</span> <span class="n">x4</span><span class="p">,</span> <span class="n">x5</span><span class="p">,</span> <span class="n">x6</span>
</span></span></code></pre></div><h3 id="611-init_cpu_task">6.1.1. init_cpu_task</h3>
<p>先看一下涉及到的几个宏。<code>arch/arm64/kernel/asm-offsets.c</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln">1</span><span class="cl"><span class="cp">#define TSK_TI_CPU 16 </span><span class="cm">/* offsetof(struct task_struct, thread_info.cpu) */</span><span class="cp">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="cp">#define TSK_STACK 32 </span><span class="cm">/* offsetof(struct task_struct, stack) */</span><span class="cp">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="cp">#define S_STACKFRAME 304 </span><span class="cm">/* offsetof(struct pt_regs, stackframe) */</span><span class="cp">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="cp">#define PT_REGS_SIZE 336 </span><span class="cm">/* sizeof(struct pt_regs) */</span><span class="cp">
</span></span></span></code></pre></div><p>在task_pt_regs(current)-&gt;stackframe创建一个最终帧记录，这样unwinder就可以根据任务堆栈中的位置来识别任何任务的最终帧记录。保留整个pt_regs空间使用户任务和kthread保持一致性。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln"> 1</span><span class="cl">    <span class="cm">/*
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="cm">     * Initialize CPU registers with task-specific and cpu-specific context.
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="cm">     *
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="cm">     * Create a final frame record at task_pt_regs(current)-&gt;stackframe, so
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="cm">     * that the unwinder can identify the final frame record of any task by
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="cm">     * its location in the task stack. We reserve the entire pt_regs space
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="cm">     * for consistency with user tasks and kthreads.
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="cm">     */</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="p">.</span><span class="n">macro</span>	<span class="n">init_cpu_task</span> <span class="n">tsk</span><span class="p">,</span> <span class="n">tmp1</span><span class="p">,</span> <span class="n">tmp2</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="n">msr</span>	<span class="n">sp_el0</span><span class="p">,</span> <span class="err">\</span><span class="n">tsk</span>                    <span class="c1">/// 将init_task的地址写入sp_el0，内核运行于EL2或EL1，内核中会使用sp_el0来作为current
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">    <span class="n">ldr</span>	<span class="err">\</span><span class="n">tmp1</span><span class="p">,</span> <span class="p">[</span><span class="err">\</span><span class="n">tsk</span><span class="p">,</span> <span class="err">#</span><span class="n">TSK_STACK</span><span class="p">]</span>       <span class="c1">/// 获取init_task的栈地址，offsetof(struct task_struct, stack)
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="c1"></span>    <span class="n">add</span>	<span class="n">sp</span><span class="p">,</span> <span class="err">\</span><span class="n">tmp1</span><span class="p">,</span> <span class="err">#</span><span class="n">THREAD_SIZE</span>         <span class="c1">/// 栈是由高地址向下生长的，所以SP_ELx要加上THREAD_SIZE，init_task的栈是静态分配的，它指向init_stack这是一个数组。
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="c1"></span>    <span class="n">sub</span>	<span class="n">sp</span><span class="p">,</span> <span class="n">sp</span><span class="p">,</span> <span class="err">#</span><span class="n">PT_REGS_SIZE</span>           <span class="c1">/// 为struct pt_regs留出空间
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">    <span class="n">stp</span>	<span class="n">xzr</span><span class="p">,</span> <span class="n">xzr</span><span class="p">,</span> <span class="p">[</span><span class="n">sp</span><span class="p">,</span> <span class="err">#</span><span class="n">S_STACKFRAME</span><span class="p">]</span>   <span class="c1">/// 将struct pt_regs的u64 stackframe[2]清零
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="c1"></span>    <span class="n">add</span>	<span class="n">x29</span><span class="p">,</span> <span class="n">sp</span><span class="p">,</span> <span class="err">#</span><span class="n">S_STACKFRAME</span>          <span class="c1">/// x29(FP)指向栈中pt_regs的stackframe
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">    <span class="n">scs_load</span> <span class="err">\</span><span class="n">tsk</span>                       	<span class="c1">/// 此处为空操作，详细信息可以参考arch/Kconfig中的SHADOW_CALL_STACK
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">    <span class="n">adr_l</span>	<span class="err">\</span><span class="n">tmp1</span><span class="p">,</span> <span class="n">__per_cpu_offset</span>     	<span class="c1">/// 读取__per_cpu_offset[NR_CPUS]数组基地址
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="c1"></span>    <span class="n">ldr</span>	<span class="n">w</span><span class="err">\</span><span class="n">tmp2</span><span class="p">,</span> <span class="p">[</span><span class="err">\</span><span class="n">tsk</span><span class="p">,</span> <span class="err">#</span><span class="n">TSK_TI_CPU</span><span class="p">]</span>     <span class="c1">/// offsetof(struct task_struct, thread_info.cpu)
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="c1"></span>    <span class="n">ldr</span>	<span class="err">\</span><span class="n">tmp1</span><span class="p">,</span> <span class="p">[</span><span class="err">\</span><span class="n">tmp1</span><span class="p">,</span> <span class="err">\</span><span class="n">tmp2</span><span class="p">,</span> <span class="n">lsl</span> <span class="err">#</span><span class="mi">3</span><span class="p">]</span>   <span class="c1">/// tmp1 = __per_cpu_offset[init_task.cpu &lt;&lt; 3]，通常来说，bootcpu为0
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="c1"></span>    <span class="n">set_this_cpu_offset</span> <span class="err">\</span><span class="n">tmp1</span>           	<span class="c1">/// 将当前cpu的per_cpu变量的offset值写入TPIDR_ELx
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="c1"></span>    <span class="p">.</span><span class="n">endm</span>
</span></span></code></pre></div><p>几个寄存器的最终结果：</p>
<blockquote>
<p>SP_EL0 = &amp;init_task
SP_ELx = init_task.stack + THREAD_SIZE - sizeof(struct pt_regs)
x29(FP) = SP_ELx + S_STACKFRAME</p>
</blockquote>
<h2 id="62-中断向量基地址寄存器配置">6.2. 中断向量基地址寄存器配置</h2>
<p>将中断向量表的起始虚拟地址写入到VBAR_EL1。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln">1</span><span class="cl">    <span class="n">adr_l</span>	<span class="n">x8</span><span class="p">,</span> <span class="n">vectors</span>			<span class="c1">// load VBAR_EL1 with virtual
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"></span>    <span class="n">msr</span>	<span class="n">vbar_el1</span><span class="p">,</span> <span class="n">x8</span>			<span class="c1">// vector table address
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="c1"></span>    <span class="n">isb</span>
</span></span></code></pre></div><h2 id="63-备份fp和lr寄存器">6.3. 备份fp和lr寄存器</h2>
<p>此时sp的值为 <code>init_task.stack + THREAD_SIZE - sizeof(struct pt_regs)</code>。主要工作如下：</p>
<ol>
<li>将x29(FP)和x30(LR)分别保存到 <code>sp-16</code>和 <code>sp-8</code>的地址上，然后 <code>sp -= 16</code>。</li>
<li>将sp的值写入到x29(FP)</li>
</ol>
<p>这是实现了ARM64函数调用标准规定的栈布局，为后续函数调用的入栈出栈做好了准备。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln">1</span><span class="cl">    <span class="n">stp</span>	<span class="n">x29</span><span class="p">,</span> <span class="n">x30</span><span class="p">,</span> <span class="p">[</span><span class="n">sp</span><span class="p">,</span> <span class="err">#</span><span class="o">-</span><span class="mi">16</span><span class="p">]</span><span class="o">!</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">    <span class="n">mov</span>	<span class="n">x29</span><span class="p">,</span> <span class="n">sp</span>
</span></span></code></pre></div><h2 id="64-保存设备树物理地址到__fdt_pointer">6.4. 保存设备树物理地址到__fdt_pointer</h2>
<p><code>__fdt_pointer</code>是一个全局变量，在后续进行FDT映射时需要用到。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln">1</span><span class="cl">    <span class="n">str_l</span>	<span class="n">x21</span><span class="p">,</span> <span class="n">__fdt_pointer</span><span class="p">,</span> <span class="n">x5</span>		<span class="c1">// Save FDT pointer
</span></span></span></code></pre></div><h2 id="65-计算kimage_voffset">6.5. 计算kimage_voffset</h2>
<p><code>arch/arm64/mm/mmu.c</code>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">u64</span> <span class="n">kimage_vaddr</span> <span class="n">__ro_after_init</span> <span class="o">=</span> <span class="p">(</span><span class="n">u64</span><span class="p">)</span><span class="o">&amp;</span><span class="n">_text</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nf">EXPORT_SYMBOL</span><span class="p">(</span><span class="n">kimage_vaddr</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="n">u64</span> <span class="n">kimage_voffset</span> <span class="n">__ro_after_init</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="nf">EXPORT_SYMBOL</span><span class="p">(</span><span class="n">kimage_voffset</span><span class="p">);</span>
</span></span></code></pre></div><p><code>kimage_voffset</code>记录了内核镜像映射后的虚拟地址与内核镜像在内存中的物理地址之间的差值。<code>kimage_vaddr</code>记录了 <code>_text</code>的链接地址，也就是最终 <code>_text</code>的虚拟地址（如果使能了CONFIG_RELOCATE，此值会在运行过程中改变），x0作为传入参数记录了 <code>_text</code>的物理地址，相减即可得到 <code>kimage_voffset</code>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln">1</span><span class="cl">    <span class="n">ldr_l</span>	<span class="n">x4</span><span class="p">,</span> <span class="n">kimage_vaddr</span>		<span class="c1">// Save the offset between
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"></span>    <span class="n">sub</span>	<span class="n">x4</span><span class="p">,</span> <span class="n">x4</span><span class="p">,</span> <span class="n">x0</span>			<span class="c1">// the kernel virtual and
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="c1"></span>    <span class="n">str_l</span>	<span class="n">x4</span><span class="p">,</span> <span class="n">kimage_voffset</span><span class="p">,</span> <span class="n">x5</span>		<span class="c1">// physical mappings
</span></span></span></code></pre></div><h2 id="66-set_cpu_boot_mode_flag">6.6. set_cpu_boot_mode_flag</h2>
<p>将启动时的特权级别保存到__boot_cpu_mode[2]全局数组。
早期版本的内核set_cpu_boot_mode_flag会在primary_entry中执行，set_cpu_boot_mode_flag会在ret之前调用 <code>dmb sy</code>和 <code>dc  ivac, x1</code>来保证指令执行顺序和使高速缓存失效。
6.1版本内核改到了在 <code>__primary_switched</code>中执行，等到清空bss段后，通过 <code>dsb	ishst</code>完成高速缓存的失效操作。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="cm">/*
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="cm"> * Sets the __boot_cpu_mode flag depending on the CPU boot mode passed
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="cm"> * in w0. See arch/arm64/include/asm/virt.h for more info.
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="cm"> */</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="nf">SYM_FUNC_START_LOCAL</span><span class="p">(</span><span class="n">set_cpu_boot_mode_flag</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">    <span class="n">adr_l</span>	<span class="n">x1</span><span class="p">,</span> <span class="n">__boot_cpu_mode</span>     <span class="c1">/// x1记录__boot_cpu_mode[]的起始地址
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"></span>    <span class="n">cmp</span>	<span class="n">w0</span><span class="p">,</span> <span class="err">#</span><span class="n">BOOT_CPU_MODE_EL2</span>      <span class="c1">/// w0记录启动时的异常等级
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1"></span>    <span class="n">b</span><span class="p">.</span><span class="n">ne</span>	<span class="mf">1f</span>                      <span class="c1">/// 如果不是从EL2启动，则跳转到1处
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1"></span>    <span class="n">add</span>	<span class="n">x1</span><span class="p">,</span> <span class="n">x1</span><span class="p">,</span> <span class="err">#</span><span class="mi">4</span>                  <span class="c1">/// 如果是从EL2启动，地址指向__boot_cpu_mode[1]
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"></span>    <span class="c1">/// 保存启动模式到x1指向的地址，如果是从EL1启动，地址指向__boot_cpu_mode[0]
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c1"></span><span class="mi">1</span><span class="o">:</span>	<span class="n">str</span>	<span class="n">w0</span><span class="p">,</span> <span class="p">[</span><span class="n">x1</span><span class="p">]</span>			<span class="c1">// Save CPU boot mode
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c1"></span>    <span class="n">ret</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="nf">SYM_FUNC_END</span><span class="p">(</span><span class="n">set_cpu_boot_mode_flag</span><span class="p">)</span>
</span></span></code></pre></div><p>简化的C语言伪代码：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln">1</span><span class="cl">    <span class="n">u32</span> <span class="o">*</span><span class="n">x1</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">__boot_cpu_mode</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">w0</span> <span class="o">==</span> <span class="n">BOOT_CPU_MODE_EL2</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">        <span class="n">x1</span><span class="o">++</span><span class="p">;</span>   <span class="c1">// *x1 = &amp;__boot_cpu_mode[1]
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"></span>    <span class="p">}</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">    <span class="o">*</span><span class="n">x1</span> <span class="o">=</span> <span class="n">w0</span><span class="p">;</span>
</span></span></code></pre></div><p>最终结果与如下代码相同</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln">1</span><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">w0</span> <span class="o">==</span> <span class="n">BOOT_CPU_MODE_EL2</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">        <span class="n">__boot_cpu_mode</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">BOOT_CPU_MODE_EL2</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">        <span class="n">__boot_cpu_mode</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">BOOT_CPU_MODE_EL1</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">    <span class="p">}</span>
</span></span></code></pre></div><h2 id="67-清空bss段">6.7. 清空BSS段</h2>
<p>清空BSS后，要使高速缓存失效。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln">1</span><span class="cl">    <span class="c1">// Clear BSS
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"></span>    <span class="n">adr_l</span>	<span class="n">x0</span><span class="p">,</span> <span class="n">__bss_start</span> <span class="c1">/// 起始地址
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="c1"></span>    <span class="n">mov</span>	<span class="n">x1</span><span class="p">,</span> <span class="n">xzr</span>             <span class="c1">/// 要写入的值，xzr是一个特殊的寄存器，值为64位的0
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"></span>    <span class="n">adr_l</span>	<span class="n">x2</span><span class="p">,</span> <span class="n">__bss_stop</span>  <span class="c1">/// 结束地址
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1"></span>    <span class="n">sub</span>	<span class="n">x2</span><span class="p">,</span> <span class="n">x2</span><span class="p">,</span> <span class="n">x0</span>          <span class="c1">/// size = __bss_stop - __bss_start
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="c1"></span>    <span class="n">bl</span>	<span class="n">__pi_memset</span>         <span class="c1">/// memset(x0, x1, x2)
</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="c1"></span>    <span class="n">dsb</span>	<span class="n">ishst</span>				<span class="c1">// Make zero page visible to PTW
</span></span></span></code></pre></div><h2 id="68-一些其他工作">6.8. 一些其他工作</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="cp">#if VA_BITS &gt; 48
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="cp"></span>    <span class="n">adr_l</span>	<span class="n">x8</span><span class="p">,</span> <span class="n">vabits_actual</span>		<span class="c1">// Set this early so KASAN early init
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1"></span>    <span class="n">str</span>	<span class="n">x25</span><span class="p">,</span> <span class="p">[</span><span class="n">x8</span><span class="p">]</span>			<span class="c1">// ... observes the correct value
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"></span>    <span class="n">dc</span>	<span class="n">civac</span><span class="p">,</span> <span class="n">x8</span>			<span class="c1">// Make visible to booting secondaries
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1"></span><span class="cp">#endif
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="cp">#ifdef CONFIG_RANDOMIZE_BASE
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="cp"></span>    <span class="n">adrp</span>	<span class="n">x5</span><span class="p">,</span> <span class="n">memstart_offset_seed</span>	<span class="c1">// Save KASLR linear map seed
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1"></span>    <span class="n">strh</span>	<span class="n">w24</span><span class="p">,</span> <span class="p">[</span><span class="n">x5</span><span class="p">,</span> <span class="o">:</span><span class="nl">lo12</span><span class="p">:</span><span class="n">memstart_offset_seed</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="cp">#endif
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="cp">#if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="cp"></span>    <span class="n">bl</span>	<span class="n">kasan_early_init</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="cp">#endif
</span></span></span></code></pre></div><h2 id="69-early_fdt_map">6.9. early_fdt_map</h2>
<p>尝试建立fixmap的页表，可能失败，后边init_feature_override会用到。
如果失败，会在setup_arch通过early_fixmap_init重新映射。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln">1</span><span class="cl">    <span class="n">mov</span>	<span class="n">x0</span><span class="p">,</span> <span class="n">x21</span>				<span class="c1">// pass FDT address in x0
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"></span>    <span class="n">bl</span>	<span class="n">early_fdt_map</span>			<span class="c1">// Try mapping the FDT early
</span></span></span></code></pre></div><h2 id="610-init_feature_override">6.10. init_feature_override</h2>
<p>根据BootLoader传入的bootargs参数，对一些参数的改写。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln">1</span><span class="cl">    <span class="n">mov</span>	<span class="n">x0</span><span class="p">,</span> <span class="n">x20</span>				<span class="c1">// pass the full boot status
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"></span>    <span class="n">bl</span>	<span class="n">init_feature_override</span>		<span class="c1">// Parse cpu feature overrides
</span></span></span></code></pre></div><h2 id="611-finalise_el2">6.11. finalise_el2</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln">1</span><span class="cl">    <span class="n">mov</span>	<span class="n">x0</span><span class="p">,</span> <span class="n">x20</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">    <span class="n">bl</span>	<span class="n">finalise_el2</span>			<span class="c1">// Prefer VHE if possible
</span></span></span></code></pre></div><h2 id="612-跳转start_kernel">6.12. 跳转start_kernel</h2>
<p>从栈中恢复x29(FP)和x30(LR)，sp重新指向 <code>init_task.stack + THREAD_SIZE - sizeof(struct pt_regs)</code>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln">1</span><span class="cl">    <span class="n">ldp</span>	<span class="n">x29</span><span class="p">,</span> <span class="n">x30</span><span class="p">,</span> <span class="p">[</span><span class="n">sp</span><span class="p">],</span> <span class="err">#</span><span class="mi">16</span>     <span class="c1">/// 从栈中恢复x29(FP)和x30(LR)，sp += 16
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"></span>    <span class="n">bl</span>	<span class="n">start_kernel</span>            <span class="c1">/// 跳转到start_kernel
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="c1"></span>    <span class="nf">ASM_BUG</span><span class="p">()</span>                   <span class="c1">/// 如果start_kernel返回到这里说明出错了
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"></span><span class="nf">SYM_FUNC_END</span><span class="p">(</span><span class="n">__primary_switched</span><span class="p">)</span>
</span></span></code></pre></div><h1 id="7-参考资料">7. 参考资料</h1>
<ol>
<li><code>Documentation/arm64/booting.rst</code></li>
<li><code>Documentation/translations/zh_CN/arm64/booting.txt</code></li>
<li><a href="http://blog.chinaunix.net/uid-69947851-id-5830505.html">Linux 内核启动分析-BugMan-ChinaUnix博客</a></li>
<li><a href="https://blog.csdn.net/jasonactions/article/details/115689462">中断管理基础学习笔记 - 5.1 ARM64底层中断处理</a></li>
<li><a href="https://blog.csdn.net/m0_60626268/article/details/119812726">Linux kernel ARM64 寄存器tpidr_el1 的用处</a></li>
<li><a href="https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f6f0c4362f070cab4a0cec432e82428d702ce0a6">arm64: Extract early FDT mapping from kaslr_early_init()</a></li>
<li><a href="https://blog.csdn.net/jasonactions/article/details/111383235">kernel启动流程-head.S的执行_4.el2_setup_kernel5.10 内核流程</a></li>
</ol>

    </div><div class="post_comments">
  
  
  
</div>



  </article>
<aside class="sidebar">
  <section class="sidebar_inner">
    <br>
    
  
  <div class="search">
    <input type="search" class="search_field form_field" placeholder='搜索内容...' id="find" autocomplete="off" data-scope='post'>
    <label for="find" class="search_label"><svg class="icon">
  <title>search</title>
  <use xlink:href="#search"></use>
</svg>

    </label>
    
    <div class="search_results results"></div>
  </div>

        <div class="author_header">
          <img src="https://kingdix10.gitee.io/logos/icon_yellow_transparent.png" alt="kingdix10 photo">
          <h2>kingdix10</h2>
        </div>
      <div class="author_bio">
        拾月国度
      </div>
      <a href='https://kingdix10.gitee.io/about/' class="button mt-1" role="button" title='阅读更多'>阅读更多</a>

    
      
    
    <h2 class="mt-4">最新文章</h2>
    <ul class="flex-column">
      <li>
        <a href="https://kingdix10.gitee.io/post/kernel/linux-6.6/kdump_crash/arm64_setup/" class="nav-link" title="ARM64 Crash调试环境搭建">ARM64 Crash调试环境搭建</a>
      </li>
      <li>
        <a href="https://kingdix10.gitee.io/post/kernel/linux-6.6/kdump_crash/help/" class="nav-link" title="Crash帮助信息">Crash帮助信息</a>
      </li>
      <li>
        <a href="https://kingdix10.gitee.io/post/kernel/linux-6.1/thermal/thermal/" class="nav-link" title="linux thermal子系统简介">linux thermal子系统简介</a>
      </li>
      <li>
        <a href="https://kingdix10.gitee.io/post/kernel/linux-6.1/time/clocksource_clockevent/" class="nav-link" title="linux内核时间子系统（三）">linux内核时间子系统（三）</a>
      </li>
      <li>
        <a href="https://kingdix10.gitee.io/post/kernel/linux-6.1/time/timekeeping/" class="nav-link" title="linux内核时间子系统（二）">linux内核时间子系统（二）</a>
      </li>
      <li>
        <a href="https://kingdix10.gitee.io/post/kernel/linux-6.1/time/tim/" class="nav-link" title="linux内核时间子系统（一）">linux内核时间子系统（一）</a>
      </li>
      <li>
        <a href="https://kingdix10.gitee.io/post/kernel/linux-6.1/device_driver/device_model/" class="nav-link" title="Linux内核驱动模型（四）核心结构体">Linux内核驱动模型（四）核心结构体</a>
      </li>
      <li>
        <a href="https://kingdix10.gitee.io/post/kernel/linux-6.1/device_driver/initcall/" class="nav-link" title="Linux内核驱动模型（三）驱动初始化">Linux内核驱动模型（三）驱动初始化</a>
      </li>
      <li>
        <a href="https://kingdix10.gitee.io/post/kernel/linux-6.1/fdt/dt_to_dt/" class="nav-link" title="Linux内核驱动模型（二）设备树转换">Linux内核驱动模型（二）设备树转换</a>
      </li>
      <li>
        <a href="https://kingdix10.gitee.io/post/kernel/linux-6.1/fdt/fdt_dt/" class="nav-link" title="Linux内核驱动模型（一）设备树展开">Linux内核驱动模型（一）设备树展开</a>
      </li>
    </ul>
    <div>
      <h2 class="mt-4 taxonomy" id="categories-section">分类</h2>
      <nav class="tags_nav">
        <a href='https://kingdix10.gitee.io/categories/%E8%BF%9B%E7%A8%8B%E7%AE%A1%E7%90%86/' class="post_tag button button_translucent" title="进程管理">
          进程管理
          <span class="button_tally">11</span>
        </a>
        
        <a href='https://kingdix10.gitee.io/categories/%E8%AE%BE%E5%A4%87%E6%A8%A1%E5%9E%8B/' class="post_tag button button_translucent" title="设备模型">
          设备模型
          <span class="button_tally">5</span>
        </a>
        
        <a href='https://kingdix10.gitee.io/categories/4412/' class="post_tag button button_translucent" title="4412">
          4412
          <span class="button_tally">3</span>
        </a>
        
        <a href='https://kingdix10.gitee.io/categories/c%E8%AF%AD%E8%A8%80/' class="post_tag button button_translucent" title="c语言">
          C语言
          <span class="button_tally">3</span>
        </a>
        
        <a href='https://kingdix10.gitee.io/categories/%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/' class="post_tag button button_translucent" title="启动流程">
          启动流程
          <span class="button_tally">3</span>
        </a>
        
        <a href='https://kingdix10.gitee.io/categories/%E6%97%B6%E9%97%B4%E5%AD%90%E7%B3%BB%E7%BB%9F/' class="post_tag button button_translucent" title="时间子系统">
          时间子系统
          <span class="button_tally">3</span>
        </a>
        
        <a href='https://kingdix10.gitee.io/categories/kdump-crash/' class="post_tag button button_translucent" title="kdump-crash">
          KDUMP-CRASH
          <span class="button_tally">2</span>
        </a>
        
        <a href='https://kingdix10.gitee.io/categories/%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA/' class="post_tag button button_translucent" title="环境搭建">
          环境搭建
          <span class="button_tally">2</span>
        </a>
        
        <a href='https://kingdix10.gitee.io/categories/linux%E5%91%BD%E4%BB%A4/' class="post_tag button button_translucent" title="linux命令">
          LINUX命令
          <span class="button_tally">1</span>
        </a>
        
        <a href='https://kingdix10.gitee.io/categories/thermal%E5%AD%90%E7%B3%BB%E7%BB%9F/' class="post_tag button button_translucent" title="thermal子系统">
          THERMAL子系统
          <span class="button_tally">1</span>
        </a>
        
        <a href='https://kingdix10.gitee.io/categories/%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86/' class="post_tag button button_translucent" title="内存管理">
          内存管理
          <span class="button_tally">1</span>
        </a>
        
        <a href='https://kingdix10.gitee.io/categories/%E5%86%85%E6%A0%B8%E5%9F%BA%E7%A1%80/' class="post_tag button button_translucent" title="内核基础">
          内核基础
          <span class="button_tally">1</span>
        </a>
        
        <a href='https://kingdix10.gitee.io/categories/%E5%86%85%E6%A0%B8%E8%B0%83%E8%AF%95/' class="post_tag button button_translucent" title="内核调试">
          内核调试
          <span class="button_tally">1</span>
        </a>
        
        
      </nav>
    </div>
    <div>
      <h2 class="mt-4 taxonomy" id="series-section">专栏</h2>
      <nav class="tags_nav">
        <a href='https://kingdix10.gitee.io/series/%E8%BF%9B%E7%A8%8B%E7%AE%A1%E7%90%86/' class="post_tag button button_translucent" title="进程管理">
          进程管理
          <span class="button_tally">11</span>
        </a>
        
        <a href='https://kingdix10.gitee.io/series/%E8%AE%BE%E5%A4%87%E9%A9%B1%E5%8A%A8%E6%A8%A1%E5%9E%8B/' class="post_tag button button_translucent" title="设备驱动模型">
          设备驱动模型
          <span class="button_tally">5</span>
        </a>
        
        <a href='https://kingdix10.gitee.io/series/%E6%97%B6%E9%97%B4%E5%AD%90%E7%B3%BB%E7%BB%9F/' class="post_tag button button_translucent" title="时间子系统">
          时间子系统
          <span class="button_tally">3</span>
        </a>
        
        
      </nav>
    </div>
    <div>
      <h2 class="mt-4 taxonomy" id="tags-section">标签</h2>
      <nav class="tags_nav">
        <a href='https://kingdix10.gitee.io/tags/linux%E5%86%85%E6%A0%B8/' class="post_tag button button_translucent" title="linux内核">
          LINUX内核
          <span class="button_tally">19</span>
        </a>
        
        <a href='https://kingdix10.gitee.io/tags/linux-6.1/' class="post_tag button button_translucent" title="linux-6.1">
          LINUX-6.1
          <span class="button_tally">12</span>
        </a>
        
        <a href='https://kingdix10.gitee.io/tags/linux-6.6/' class="post_tag button button_translucent" title="linux-6.6">
          LINUX-6.6
          <span class="button_tally">9</span>
        </a>
        
        <a href='https://kingdix10.gitee.io/tags/%E5%86%85%E6%A0%B8%E9%A9%B1%E5%8A%A8/' class="post_tag button button_translucent" title="内核驱动">
          内核驱动
          <span class="button_tally">9</span>
        </a>
        
        <a href='https://kingdix10.gitee.io/tags/%E8%BF%9B%E7%A8%8B%E7%AE%A1%E7%90%86/' class="post_tag button button_translucent" title="进程管理">
          进程管理
          <span class="button_tally">6</span>
        </a>
        
        <a href='https://kingdix10.gitee.io/tags/linux/' class="post_tag button button_translucent" title="linux">
          LINUX
          <span class="button_tally">5</span>
        </a>
        
        <a href='https://kingdix10.gitee.io/tags/4412/' class="post_tag button button_translucent" title="4412">
          4412
          <span class="button_tally">3</span>
        </a>
        
        <a href='https://kingdix10.gitee.io/tags/c%E8%AF%AD%E8%A8%80/' class="post_tag button button_translucent" title="c语言">
          C语言
          <span class="button_tally">3</span>
        </a>
        
        <a href='https://kingdix10.gitee.io/tags/%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/' class="post_tag button button_translucent" title="启动流程">
          启动流程
          <span class="button_tally">3</span>
        </a>
        
        <a href='https://kingdix10.gitee.io/tags/%E5%B7%A5%E5%85%B7/' class="post_tag button button_translucent" title="工具">
          工具
          <span class="button_tally">3</span>
        </a>
        
        <a href='https://kingdix10.gitee.io/tags/kdump-crash/' class="post_tag button button_translucent" title="kdump-crash">
          KDUMP-CRASH
          <span class="button_tally">2</span>
        </a>
        
        <a href='https://kingdix10.gitee.io/tags/%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA/' class="post_tag button button_translucent" title="环境搭建">
          环境搭建
          <span class="button_tally">2</span>
        </a>
        
        <a href='https://kingdix10.gitee.io/tags/arm64/' class="post_tag button button_translucent" title="arm64">
          ARM64
          <span class="button_tally">1</span>
        </a>
        
        <a href='https://kingdix10.gitee.io/tags/u-boot/' class="post_tag button button_translucent" title="u-boot">
          U-BOOT
          <span class="button_tally">1</span>
        </a>
        
        <a href='https://kingdix10.gitee.io/tags/%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86/' class="post_tag button button_translucent" title="内存管理">
          内存管理
          <span class="button_tally">1</span>
        </a>
        
        <a href='https://kingdix10.gitee.io/tags/%E5%86%85%E6%A0%B8%E8%B0%83%E8%AF%95/' class="post_tag button button_translucent" title="内核调试">
          内核调试
          <span class="button_tally">1</span>
        </a>
        
        <a href='https://kingdix10.gitee.io/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/' class="post_tag button button_translucent" title="数据结构">
          数据结构
          <span class="button_tally">1</span>
        </a>
        
        <a href='https://kingdix10.gitee.io/tags/%E8%B0%83%E8%AF%95/' class="post_tag button button_translucent" title="调试">
          调试
          <span class="button_tally">1</span>
        </a>
        
        
      </nav>
    </div>
  </section>
</aside>

  
</div>
    </main><svg width="0" height="0" class="hidden">
  <symbol viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg" id="facebook">
    <path d="M437 0H75C33.648 0 0 33.648 0 75v362c0 41.352 33.648 75 75 75h151V331h-60v-90h60v-61c0-49.629 40.371-90 90-90h91v90h-91v61h91l-15 90h-76v181h121c41.352 0 75-33.648 75-75V75c0-41.352-33.648-75-75-75zm0 0"></path>
  </symbol>
  <symbol xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18.001 18.001" id="twitter">
    <path d="M15.891 4.013c.808-.496 1.343-1.173 1.605-2.034a8.68 8.68 0 0 1-2.351.861c-.703-.756-1.593-1.14-2.66-1.14-1.043 0-1.924.366-2.643 1.078a3.56 3.56 0 0 0-1.076 2.605c0 .309.039.585.117.819-3.076-.105-5.622-1.381-7.628-3.837-.34.601-.51 1.213-.51 1.846 0 1.301.549 2.332 1.645 3.089-.625-.053-1.176-.211-1.645-.47 0 .929.273 1.705.82 2.388a3.623 3.623 0 0 0 2.115 1.291c-.312.08-.641.118-.979.118-.312 0-.533-.026-.664-.083.23.757.664 1.371 1.291 1.841a3.652 3.652 0 0 0 2.152.743C4.148 14.173 2.625 14.69.902 14.69c-.422 0-.721-.006-.902-.038 1.697 1.102 3.586 1.649 5.676 1.649 2.139 0 4.029-.542 5.674-1.626 1.645-1.078 2.859-2.408 3.639-3.974a10.77 10.77 0 0 0 1.172-4.892v-.468a7.788 7.788 0 0 0 1.84-1.921 8.142 8.142 0 0 1-2.11.593z"
      ></path>
  </symbol>
  <symbol aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" id="mail">
    <path  d="M502.3 190.8c3.9-3.1 9.7-.2 9.7 4.7V400c0 26.5-21.5 48-48 48H48c-26.5 0-48-21.5-48-48V195.6c0-5 5.7-7.8 9.7-4.7 22.4 17.4 52.1 39.5 154.1 113.6 21.1 15.4 56.7 47.8 92.2 47.6 35.7.3 72-32.8 92.3-47.6 102-74.1 131.6-96.3 154-113.7zM256 320c23.2.4 56.6-29.2 73.4-41.4 132.7-96.3 142.8-104.7 173.4-128.7 5.8-4.5 9.2-11.5 9.2-18.9v-19c0-26.5-21.5-48-48-48H48C21.5 64 0 85.5 0 112v19c0 7.4 3.4 14.3 9.2 18.9 30.6 23.9 40.7 32.4 173.4 128.7 16.8 12.2 50.2 41.8 73.4 41.4z"></path>
  </symbol>
  <symbol xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" id="calendar">
    <path d="M452 40h-24V0h-40v40H124V0H84v40H60C26.916 40 0 66.916 0 100v352c0 33.084 26.916 60 60 60h392c33.084 0 60-26.916 60-60V100c0-33.084-26.916-60-60-60zm20 412c0 11.028-8.972 20-20 20H60c-11.028 0-20-8.972-20-20V188h432v264zm0-304H40v-48c0-11.028 8.972-20 20-20h24v40h40V80h264v40h40V80h24c11.028 0 20 8.972 20 20v48z"></path>
    <path d="M76 230h40v40H76zm80 0h40v40h-40zm80 0h40v40h-40zm80 0h40v40h-40zm80 0h40v40h-40zM76 310h40v40H76zm80 0h40v40h-40zm80 0h40v40h-40zm80 0h40v40h-40zM76 390h40v40H76zm80 0h40v40h-40zm80 0h40v40h-40zm80 0h40v40h-40zm80-80h40v40h-40z"></path>
  </symbol>
  <symbol xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" id="github">
    <path d="M255.968 5.329C114.624 5.329 0 120.401 0 262.353c0 113.536 73.344 209.856 175.104 243.872 12.8 2.368 17.472-5.568 17.472-12.384 0-6.112-.224-22.272-.352-43.712-71.2 15.52-86.24-34.464-86.24-34.464-11.616-29.696-28.416-37.6-28.416-37.6-23.264-15.936 1.728-15.616 1.728-15.616 25.696 1.824 39.2 26.496 39.2 26.496 22.848 39.264 59.936 27.936 74.528 21.344 2.304-16.608 8.928-27.936 16.256-34.368-56.832-6.496-116.608-28.544-116.608-127.008 0-28.064 9.984-51.008 26.368-68.992-2.656-6.496-11.424-32.64 2.496-68 0 0 21.504-6.912 70.4 26.336 20.416-5.696 42.304-8.544 64.096-8.64 21.728.128 43.648 2.944 64.096 8.672 48.864-33.248 70.336-26.336 70.336-26.336 13.952 35.392 5.184 61.504 2.56 68 16.416 17.984 26.304 40.928 26.304 68.992 0 98.72-59.84 120.448-116.864 126.816 9.184 7.936 17.376 23.616 17.376 47.584 0 34.368-.32 62.08-.32 70.496 0 6.88 4.608 14.88 17.6 12.352C438.72 472.145 512 375.857 512 262.353 512 120.401 397.376 5.329 255.968 5.329z"></path>
  </symbol>

  <symbol width="128px" height="128px" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg" id="gitee">
      <g id="LOGO" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
          <g id="Artboard-7" transform="translate(-192.000000, -115.000000)" fill="#C71D23">
              <path d="M228,115 C247.882251,115 264,131.117749 264,151 C264,170.882251 247.882251,187 228,187 C208.117749,187 192,170.882251 192,151 C192,131.117749 208.117749,115 228,115 Z M246.223335,131 C246.222967,131 246.2226,131 246.222232,131.001102 L221.33326,131.001102 C213.969504,131.001102 208,136.970606 208,144.334362 L208,169.222232 C208,170.204066 208.795934,171 209.777768,171 L236.000329,171 C242.627564,171 248,165.627564 248,159.000329 L248,148.778425 C248,147.796591 247.204066,147.000657 246.222232,147.000657 L225.7779,147.000657 C224.796248,147.001123 224.000389,147.796773 223.999667,148.778425 L223.998503,153.222667 C223.997802,154.155409 224.715909,154.920565 225.629522,154.994969 L225.775805,155.00042 L225.775805,155.00042 L238.222276,155.000317 C239.155019,155.000295 239.919992,155.718618 239.994164,156.63225 L240.000044,156.77807 L240.000044,156.77807 L240.000044,157.666909 C240.000044,160.612411 237.612243,163.000213 234.66674,163.000213 L217.776621,163.000213 C216.794928,163.000164 215.999101,162.204358 215.999025,161.222665 L215.998559,144.334184 C215.998337,141.462319 218.268172,139.120556 221.111731,139.005187 L221.331716,139.00088 L221.331716,139.00088 L246.21727,139.00088 C247.198674,138.999777 247.994429,138.204515 247.996141,137.223112 L247.998897,132.77887 C248.000609,131.797037 247.205169,131.000609 246.223335,131 Z" id="logo_gitee_g_red"></path>
          </g>
      </g>
  </symbol>

  <symbol xmlns="http://www.w3.org/2000/svg" viewBox="0 0 212 212" id="gitlab">
    <path d="M12.3 74.7h54L43.3 3c-1-3.6-6.4-3.6-7.6 0L12.3 74.8z" />
    <path d="M12.3 74.7L.5 111c-1 3.2 0 6.8 3 8.8l101.6 74-92.5-119z"/>
    <path d="M105 193.7l-38.6-119h-54l92.7 119z"/>
    <path d="M105 193.7l38.7-119H66.4l38.7 119z"/>
    <path d="M105 193.7l38.7-119H198l-93 119z"/>
    <path d="M198 74.7l11.6 36.2c1 3 0 6.6-3 8.6l-101.5 74 93-119z"/>
    <path d="M198 74.7h-54.3L167 3c1.2-3.6 6.4-3.6 7.6 0L198 74.8z"/>
  </symbol>
  <symbol viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" id="rss">
    <circle cx="3.429" cy="20.571" r="3.429"></circle>
    <path d="M11.429 24h4.57C15.999 15.179 8.821 8.001 0 8v4.572c6.302.001 11.429 5.126 11.429 11.428z"></path>
    <path d="M24 24C24 10.766 13.234 0 0 0v4.571c10.714 0 19.43 8.714 19.43 19.429z"></path>
  </symbol>
  <symbol viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg" id="linkedin">
    <path d="M437 0H75C33.648 0 0 33.648 0 75v362c0 41.352 33.648 75 75 75h362c41.352 0 75-33.648 75-75V75c0-41.352-33.648-75-75-75zM181 406h-60V196h60zm0-240h-60v-60h60zm210 240h-60V286c0-16.54-13.46-30-30-30s-30 13.46-30 30v120h-60V196h60v11.309C286.719 202.422 296.93 196 316 196c40.691.043 75 36.547 75 79.688zm0 0"></path>
  </symbol>
  <symbol xmlns="http://www.w3.org/2000/svg" viewBox="0 0 612 612" id="to-top">
    <path d="M604.501 440.509L325.398 134.956c-5.331-5.357-12.423-7.627-19.386-7.27-6.989-.357-14.056 1.913-19.387 7.27L7.499 440.509c-9.999 10.024-9.999 26.298 0 36.323s26.223 10.024 36.222 0l262.293-287.164L568.28 476.832c9.999 10.024 26.222 10.024 36.221 0 9.999-10.023 9.999-26.298 0-36.323z"></path>
  </symbol>
  <symbol viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg" id="carly">
    <path d="M504.971 239.029L448 182.059V84c0-46.317-37.682-84-84-84h-44c-13.255 0-24 10.745-24 24s10.745 24 24 24h44c19.851 0 36 16.149 36 36v108c0 6.365 2.529 12.47 7.029 16.971L454.059 256l-47.029 47.029A24.002 24.002 0 0 0 400 320v108c0 19.851-16.149 36-36 36h-44c-13.255 0-24 10.745-24 24s10.745 24 24 24h44c46.318 0 84-37.683 84-84v-98.059l56.971-56.971c9.372-9.372 9.372-24.568 0-33.941zM112 192V84c0-19.851 16.149-36 36-36h44c13.255 0 24-10.745 24-24S205.255 0 192 0h-44c-46.318 0-84 37.683-84 84v98.059l-56.971 56.97c-9.373 9.373-9.373 24.568 0 33.941L64 329.941V428c0 46.317 37.682 84 84 84h44c13.255 0 24-10.745 24-24s-10.745-24-24-24h-44c-19.851 0-36-16.149-36-36V320c0-6.365-2.529-12.47-7.029-16.971L57.941 256l47.029-47.029A24.002 24.002 0 0 0 112 192z"></path>
  </symbol>
  <symbol viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" id="copy">
    <path d="M23 2.75A2.75 2.75 0 0 0 20.25 0H8.75A2.75 2.75 0 0 0 6 2.75v13.5A2.75 2.75 0 0 0 8.75 19h11.5A2.75 2.75 0 0 0 23 16.25zM18.25 14.5h-7.5a.75.75 0 0 1 0-1.5h7.5a.75.75 0 0 1 0 1.5zm0-3h-7.5a.75.75 0 0 1 0-1.5h7.5a.75.75 0 0 1 0 1.5zm0-3h-7.5a.75.75 0 0 1 0-1.5h7.5a.75.75 0 0 1 0 1.5z"></path>
    <path d="M8.75 20.5a4.255 4.255 0 0 1-4.25-4.25V2.75c0-.086.02-.166.025-.25H3.75A2.752 2.752 0 0 0 1 5.25v16A2.752 2.752 0 0 0 3.75 24h12a2.752 2.752 0 0 0 2.75-2.75v-.75z"></path>
  </symbol>
  <symbol xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512.001 512.001" id="closeme">
    <path d="M284.286 256.002L506.143 34.144c7.811-7.811 7.811-20.475 0-28.285-7.811-7.81-20.475-7.811-28.285 0L256 227.717 34.143 5.859c-7.811-7.811-20.475-7.811-28.285 0-7.81 7.811-7.811 20.475 0 28.285l221.857 221.857L5.858 477.859c-7.811 7.811-7.811 20.475 0 28.285a19.938 19.938 0 0 0 14.143 5.857 19.94 19.94 0 0 0 14.143-5.857L256 284.287l221.857 221.857c3.905 3.905 9.024 5.857 14.143 5.857s10.237-1.952 14.143-5.857c7.811-7.811 7.811-20.475 0-28.285L284.286 256.002z"></path>
  </symbol>
  <symbol xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" id="open-menu">
    <path d="M492 236H20c-11.046 0-20 8.954-20 20s8.954 20 20 20h472c11.046 0 20-8.954 20-20s-8.954-20-20-20zm0-160H20C8.954 76 0 84.954 0 96s8.954 20 20 20h472c11.046 0 20-8.954 20-20s-8.954-20-20-20zm0 320H20c-11.046 0-20 8.954-20 20s8.954 20 20 20h472c11.046 0 20-8.954 20-20s-8.954-20-20-20z"></path>
  </symbol>
  <symbol xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" id="instagram">
    <path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zm0-2.163c-3.259 0-3.667.014-4.947.072-4.358.2-6.78 2.618-6.98 6.98-.059 1.281-.073 1.689-.073 4.948 0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98 1.281.058 1.689.072 4.948.072 3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98-1.281-.059-1.69-.073-4.949-.073zm0 5.838c-3.403 0-6.162 2.759-6.162 6.162s2.759 6.163 6.162 6.163 6.162-2.759 6.162-6.163c0-3.403-2.759-6.162-6.162-6.162zm0 10.162c-2.209 0-4-1.79-4-4 0-2.209 1.791-4 4-4s4 1.791 4 4c0 2.21-1.791 4-4 4zm6.406-11.845c-.796 0-1.441.645-1.441 1.44s.645 1.44 1.441 1.44c.795 0 1.439-.645 1.439-1.44s-.644-1.44-1.439-1.44z"/>
  </symbol>
  <symbol xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" id=youtube>
    <path d="M19.615 3.184c-3.604-.246-11.631-.245-15.23 0-3.897.266-4.356 2.62-4.385 8.816.029 6.185.484 8.549 4.385 8.816 3.6.245 11.626.246 15.23 0 3.897-.266 4.356-2.62 4.385-8.816-.029-6.185-.484-8.549-4.385-8.816zm-10.615 12.816v-8l8 3.993-8 4.007z"/>
  </symbol>
  <symbol xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" id="stackoverflow">
    <path d="M21 27v-8h3v11H0V19h3v8h18z"></path><path d="M17.1.2L15 1.8l7.9 10.6 2.1-1.6L17.1.2zm3.7 14.7L10.6 6.4l1.7-2 10.2 8.5-1.7 2zM7.2 12.3l12 5.6 1.1-2.4-12-5.6-1.1 2.4zm-1.8 6.8l13.56 1.96.17-2.38-13.26-2.55-.47 2.97zM19 25H5v-3h14v3z"></path>
  </symbol>
  <symbol xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" id="xing">
    <path d="M18.188 0c-.517 0-.741.325-.927.66 0 0-7.455 13.224-7.702 13.657.015.024 4.919 9.023 4.919 9.023.17.308.436.66.967.66h3.454c.211 0 .375-.078.463-.22.089-.151.089-.346-.009-.536l-4.879-8.916c-.004-.006-.004-.016 0-.022L22.139.756c.095-.191.097-.387.006-.535C22.056.078 21.894 0 21.686 0h-3.498zM3.648 4.74c-.211 0-.385.074-.473.216-.09.149-.078.339.02.531l2.34 4.05c.004.01.004.016 0 .021L1.86 16.051c-.099.188-.093.381 0 .529.085.142.239.234.45.234h3.461c.518 0 .766-.348.945-.667l3.734-6.609-2.378-4.155c-.172-.315-.434-.659-.962-.659H3.648v.016z"/>
  </symbol>
  <symbol xmlns="http://www.w3.org/2000/svg" viewBox="0 0 71 55" id="discord">
    <path d="M60.1045 4.8978C55.5792 2.8214 50.7265 1.2916 45.6527 0.41542C45.5603 0.39851 45.468 0.440769 45.4204 0.525289C44.7963 1.6353 44.105 3.0834 43.6209 4.2216C38.1637 3.4046 32.7345 3.4046 27.3892 4.2216C26.905 3.0581 26.1886 1.6353 25.5617 0.525289C25.5141 0.443589 25.4218 0.40133 25.3294 0.41542C20.2584 1.2888 15.4057 2.8186 10.8776 4.8978C10.8384 4.9147 10.8048 4.9429 10.7825 4.9795C1.57795 18.7309 -0.943561 32.1443 0.293408 45.3914C0.299005 45.4562 0.335386 45.5182 0.385761 45.5576C6.45866 50.0174 12.3413 52.7249 18.1147 54.5195C18.2071 54.5477 18.305 54.5139 18.3638 54.4378C19.7295 52.5728 20.9469 50.6063 21.9907 48.5383C22.0523 48.4172 21.9935 48.2735 21.8676 48.2256C19.9366 47.4931 18.0979 46.6 16.3292 45.5858C16.1893 45.5041 16.1781 45.304 16.3068 45.2082C16.679 44.9293 17.0513 44.6391 17.4067 44.3461C17.471 44.2926 17.5606 44.2813 17.6362 44.3151C29.2558 49.6202 41.8354 49.6202 53.3179 44.3151C53.3935 44.2785 53.4831 44.2898 53.5502 44.3433C53.9057 44.6363 54.2779 44.9293 54.6529 45.2082C54.7816 45.304 54.7732 45.5041 54.6333 45.5858C52.8646 46.6197 51.0259 47.4931 49.0921 48.2228C48.9662 48.2707 48.9102 48.4172 48.9718 48.5383C50.038 50.6034 51.2554 52.5699 52.5959 54.435C52.6519 54.5139 52.7526 54.5477 52.845 54.5195C58.6464 52.7249 64.529 50.0174 70.6019 45.5576C70.6551 45.5182 70.6887 45.459 70.6943 45.3942C72.1747 30.0791 68.2147 16.7757 60.1968 4.9823C60.1772 4.9429 60.1437 4.9147 60.1045 4.8978ZM23.7259 37.3253C20.2276 37.3253 17.3451 34.1136 17.3451 30.1693C17.3451 26.225 20.1717 23.0133 23.7259 23.0133C27.308 23.0133 30.1626 26.2532 30.1066 30.1693C30.1066 34.1136 27.28 37.3253 23.7259 37.3253ZM47.3178 37.3253C43.8196 37.3253 40.9371 34.1136 40.9371 30.1693C40.9371 26.225 43.7636 23.0133 47.3178 23.0133C50.9 23.0133 53.7545 26.2532 53.6986 30.1693C53.6986 34.1136 50.9 37.3253 47.3178 37.3253Z"/>
  </symbol>
  <symbol xmlns="http://www.w3.org/2000/svg" viewBox="0 0 17 18" id="mastodon">
    <path
    fill="#ffffff"
    d="m 15.054695,9.8859583 c -0.22611,1.1632697 -2.02517,2.4363497 -4.09138,2.6830797 -1.0774504,0.12856 -2.1382704,0.24673 -3.2694704,0.19484 -1.84996,-0.0848 -3.30971,-0.44157 -3.30971,-0.44157 0,0.1801 0.0111,0.35157 0.0333,0.51194 0.24051,1.82571 1.81034,1.93508 3.29737,1.98607 1.50088,0.0514 2.8373104,-0.37004 2.8373104,-0.37004 l 0.0617,1.35686 c 0,0 -1.0498104,0.56374 -2.9199404,0.66742 -1.03124,0.0567 -2.3117,-0.0259 -3.80308,-0.42069 -3.23454998,-0.85613 -3.79081998,-4.304 -3.87592998,-7.8024197 -0.026,-1.03871 -0.01,-2.01815 -0.01,-2.83732 0,-3.57732 2.34385998,-4.62587996 2.34385998,-4.62587996 1.18184,-0.54277 3.20976,-0.77101 5.318,-0.7882499985409 h 0.0518 C 9.8267646,0.01719834 11.856025,0.24547834 13.037775,0.78824834 c 0,0 2.34377,1.04855996 2.34377,4.62587996 0,0 0.0294,2.63937 -0.32687,4.47183"/>
 <path
    fill="#000000"
    d="m 12.616925,5.6916583 v 4.3315297 h -1.71607 V 5.8189683 c 0,-0.88624 -0.37289,-1.33607 -1.1187604,-1.33607 -0.82467,0 -1.23799,0.53361 -1.23799,1.58875 v 2.30122 h -1.70594 v -2.30122 c 0,-1.05514 -0.4134,-1.58875 -1.23808,-1.58875 -0.74587,0 -1.11876,0.44983 -1.11876,1.33607 v 4.2042197 h -1.71607 V 5.6916583 c 0,-0.88527 0.22541,-1.58876 0.67817,-2.10922 0.46689,-0.52047 1.07833,-0.78727 1.83735,-0.78727 0.87816,0 1.54317,0.33752 1.98288,1.01267 l 0.42744,0.71655 0.42753,-0.71655 c 0.43961,-0.67515 1.10463,-1.01267 1.9828704,-1.01267 0.75893,0 1.37037,0.2668 1.83735,0.78727 0.45268,0.52046 0.67808,1.22395 0.67808,2.10922"/>
  </symbol>
</svg>

<footer class="footer">
  <div class="footer_inner wrap pale">
    <img src='https://kingdix10.gitee.io/icons/copy.svg' class="icon icon_2 transparent" alt="拾月国度">
    <p>Copyright&nbsp;2018-&nbsp;<span class="year"></span>&nbsp;拾月国度. All Rights Reserved</p><a class="to_top" href="#documentTop">
  <svg class="icon">
  <title>to-top</title>
  <use xlink:href="#to-top"></use>
</svg>

</a>

  </div>
</footer>

<script type="text/javascript" src="https://kingdix10.gitee.io/zh-cn/js/bundle.352f6b8ac853443fc909370d968a769ffa303b36042cc66849f2d48d9e5dd951ee86c06130908f5d77ea0e55ec8fed0e5dc26f0f14bbd4ad6dcf35999232e5bc.js" integrity="sha512-NS9rishTRD/JCTcNlop2n/owOzYELMZoSfLUjZ5d2VHuhsBhMJCPXXfqDlXsj&#43;0OXcJvDxS71K1tzzWZkjLlvA==" crossorigin="anonymous"></script>

  <script src="https://kingdix10.gitee.io/js/search.min.b19f217359693afb8f0abd75ca8de6becceec41f5304950fc48e165914ef227f44d10005d02a281a66e33b482e2ff27e6eb893111505deb4f072c307e1f887bb.js"></script>

  </body>
</html>
